Floating point anomaly when unused statement is not commented out?

When the program shown below starts, it displays ok:

j= 0 9007199616606190.000000 = x k= 0 9007199616606190.000000 = [x] r= 31443101 0.000000 = m*(x-[x]) 

But when the commented line (ie //if (argc>1) r = atol(argv[1]); ) is uncommented, it produces:

  j= 20000 9007199616606190.000000 = x k= 17285 9007199616606190.000000 = [x] r= 31443101 0.000000 = m*(x-[x]) 

although this line should have no effect since argc>1 is false. Has anyone received a plausible explanation for this problem? Is it reproducible in any other systems?

  #include <stdio.h> #include <stdlib.h> #include <math.h> int main(int argc, char *argv[]) { int j, k, m=10000; double r=31443101, jroot=sqrt(83), x; //if (argc>1) r = atol(argv[1]); x = r * r * jroot; j = m*(x-floor(x)); k = floor(m*(x-floor(x))); printf ("j= %9d %24.6f = x\n", j, x); printf ("k= %9d %24.6f = [x]\n", k, floor(x)); printf ("r= %9.0f %24.6f = m*(x-[x]) \n", r, m*(x-floor(x))); return 0; } 

Note. test system = AMD Athlon 64 5200+ system with Linux 2.6.35.14-96.fc14.i686 (i.e., bootable to run 32-bit OS on 64-bit HW) with gcc (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4)

Update - A few hours ago I posted a comment that the code generated with and without the if differed only in stack offsets and some missing code. Now I believe that the comment is not entirely correct; that is, this is true for non-optimized code, but not true for the -O3 code that I executed.

Effect of the optimization switch on the problem:

  • -O0: both versions of programs work fine
  • -O2 or -O3: The commentary version has an error as described above, where j=20000 and k=17285
  • -O1: The comment version has j=20000 (error) and k=0 (OK)

In any case, looking at the -O3 -S code lists, the two cases differ mainly in skipped if code and stack offsets to the line to call floor , after which the c-if code has another fstpl than the code without the code:

  ... ;; code without comment: fmul %st, %st(1) fxch %st(1) fstpl (%esp) fxch %st(1) fstpl 48(%esp) fstpl 32(%esp) call floor movl $.LC2, (%esp) fnstcw 86(%esp) movzwl 86(%esp), %eax ... ... ;; versus code with comment: fmul %st, %st(1) fxch %st(1) fstpl (%esp) fxch %st(1) fstpl 48(%esp) fstpl 32(%esp) fstpl 64(%esp) call floor movl $.LC3, (%esp) fnstcw 102(%esp) movzwl 102(%esp), %eax ... 

I did not understand the reason for the difference.

+8
c gcc floating-point compiler-errors
source share
4 answers

Not duplicated on my system, Win7 works with CygWin with gcc 4.3.4. With and without the if , j set to zero, not 20K.

My only suggestion is to use gcc -S to look at the assembler output. This should hopefully tell you what is going wrong.

In particular, generate the assembler output into two separate files, one for the working and non-working versions, then vgrep them (close them side by side) to try to find out the difference.


This is a serious failure in your environment. If the value of m is 10000, this means that x - floor(x) should be equal to 2. I canโ€™t think about a real number for my whole life, where it would be like this :-)

+3
source share

The reason that uncommenting this line can affect the result is that without this line the compiler can see that r and jroot cannot change after initialization, so it can calculate x at compile time and not at run time. When the line is uncommented, r can change, so the calculation of x should be postponed until the execution time, which can lead to its execution with a different accuracy (especially if 387 floating-point math is used).

You can try using -mfpmath=sse -march=native to use the SSE block for floating point calculations, which does not exhibit excessive precision; or you can try using the -ffloat-store switch.

Subtracting x - floor(x) shows a catastrophic undo - this is the main cause of the problem that should be avoided;).

+2
source share

I think there are two reasons why this line may have an effect:

  • Without this line, the values โ€‹โ€‹of all these variables can be (and probably IMHO) determined at compile time; with this line, calculations should be performed at run time. But it is obvious that the pre-computed values โ€‹โ€‹of the compiler must match the values โ€‹โ€‹computed at runtime, and I tend to overlook this as the actual reason for the different observed behavior. (This will certainly show up as a huge difference in assembler output, though!)
  • On many machines, floating point arithmetic is performed using more bits in intermediate values โ€‹โ€‹than can actually be stored in a double-precision floating-point number. The second version, creating two different code paths for setting x , basically limits x to what can be stored as a double-precision floating-point number, while your first version can allow the originally calculated value for x to be available as an intermediate values โ€‹โ€‹with extra bits when calculating subsequent values. (This may be the case if all of these values โ€‹โ€‹are computed at compile time or at run time.)
+2
source share

Edition:

I also do not see the difference when I compile my code on my computer using -O0, -O1, -O2 and -O3.

AMD Phenom Quad 64 bit. gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3

I also tried clang (llvm) from version 3.0 with and without the same results.

I agree that the compiler can pre-compute everything without this line, you will probably see this at the output of the assembly.

Floating point and C can be nasty, a lot of stuff to know, to make it really work. Forced conversion of int to double conversion is useful for accuracy (c libraries in the compiler, even if fpu has good ones, as you know, there are problems and C compiler libraries that it uses, and the C library compiled or used by your program may / will differ ), but int to / from float is where the FPUs have their errors (I think I saw what was mentioned with TestFloat or something like that). You can try running TestFloat on your system to make sure your FPU is good. Between the famous pentium floating-point error in both PentiumIV and several days, most processors had floating-point errors, Pentium III I had a solid one, but Pentium IV failed. I rarely use floating points, so I am not trying to test my systems.

Optimized playback has changed your results to suit your changes, so this is most likely a gcc problem or a combination of your code and gcc (and not a hardware fpu problem). Then try using a different version of gcc on the same computer. 4.4.x instead of 4.5.x, for example.

0
source share

All Articles