On 12 May, 2013, at 22:00 , Dennis Ferguson <dennis.c.ferguson%gmail.com@localhost> wrote: [...] Attached is a program to show the problems with C99 compliance and general unhappiness caused by the current default setting of the x87 precision on NetBSD. The program evaluates 1 - ((4/x) - 1) * x for x==3.0 both as a compile time constant and at run time, and can be compiled to do so in any of the 3 floating point types. In exact arithmetic the result is precisely zero so the greater the precision with which the expression is evaluated the closer to zero the result should be. Here are the results for amd64 and i386 gcc, with the program compiled for each of the 3 floating point types and with the standard set to C99: $ gcc -std=c99 -DFLOAT -O -o try try.c $ ./try (8 0 4 1.19209e-07): compiler = -1.19209e-07 run_time = -1.19209e-07 $ gcc -std=c99 -DDOUBLE -O -o try try.c $ ./try (8 0 8 2.22045e-16): compiler = 2.22045e-16 run_time = 2.22045e-16 $ gcc -std=c99 -O -o try try.c $ ./try (8 0 16 1.08420e-19): compiler = -1.08420e-19 run_time = 2.22045e-16 $ gcc -m32 -std=c99 -DFLOAT -O -o try try.c $ ./try (4 2 4 1.19209e-07): compiler = -1.08420e-19 run_time = 2.22045e-16 $ gcc -m32 -std=c99 -DDOUBLE -O -o try try.c $ ./try (4 2 8 2.22045e-16): compiler = -1.08420e-19 run_time = 2.22045e-16 $ gcc -m32 -std=c99 -O -o try try.c $ ./try (4 2 12 1.08420e-19): compiler = -1.08420e-19 run_time = 2.22045e-16 The values in (...) are sizeof(long), FLT_EVAL_METHOD, sizeof(floating_type) used for the evaluation and XXX_EPSILON for floating_type. Results closer to 0 are more precise. Any result which is significantly larger in magnitude than XXX_EPSILON has been computed with less than the precision claimed for the type; this is the case for amd64 and i386 long double computations, and would be fixed by changing the default i387 precision. All the i386 compiler-evaluated results are consistent with the FLT_EVAL_METHOD of 2, and the value of LDBL_EPSILON, but the run time results are not. Note that while the letter of the C standard allows compile-time evaluation of constant floating point expressions to be performed more precisely than the run time, as is the case for the above i386 behaviour, it is always better if the compile-time and execution-time evaluations match precisely since that avoids having the results produced by a floating point programs change with the optimization level due to this, which in turn makes it easier to find real problems caused by the compiler's optimizer. Changing the default x87 precision to that of the 10-byte IEEE 754 format would make the C99 compile-time and run-time behaviour fully consistent, and would make the behaviour match the C99 standard where it does not now. All of this makes perfect sense to me. The only thing I don't get is that, while the compiler clearly knows how it is supposed to behave for standard C, by default it apparently instead provides "GNU" C with the following behaviour for i386: $ gcc -m32 -std=gnu99 -DFLOAT -o try try.c $ ./try (4 2 4 1.19209e-07): compiler = -1.19209e-07 run_time = 2.22045e-16 $ gcc -m32 -std=gnu99 -DDOUBLE -o try try.c $ ./try (4 2 8 2.22045e-16): compiler = 2.22045e-16 run_time = 2.22045e-16 $ gcc -m32 -std=gnu99 -o try try.c $ ./try (4 2 12 1.08420e-19): compiler = -1.08420e-19 run_time = 2.22045e-16 This does not provide an argument against changing the x87 precision since no setting of the x87 precision is going to be consistent with that and, given this, it is still better to set the x87 to fix long double and produce better arithmetic in general. I just don't get why it does that. In any case, I think changing the x87 precision to that of the 10-byte IEEE 754 extended format is exactly the right thing to do. The current state breaks long double on both architectures and is inconsistent with a standard compiler for all three floating point types for i386. Dennis Ferguson
Attachment:
try.c
Description: Binary data
Attachment:
signature.asc
Description: Message signed with OpenPGP using GPGMail