C 未注释掉未使用语句时的浮点异常?
当运行如下所示的程序时,它产生ok输出:C 未注释掉未使用语句时的浮点异常?,c,gcc,compiler-errors,floating-point,C,Gcc,Compiler Errors,Floating Point,当运行如下所示的程序时,它产生ok输出: j= 0 9007199616606190.000000 = x k= 0 9007199616606190.000000 = [x] r= 31443101 0.000000 = m*(x-[x]) 但是当注释掉的行(即,//如果(argc>1)r=atol(argv[1]);)未注释时,它会生成: j= 20000 90071996166061
j= 0 9007199616606190.000000 = x
k= 0 9007199616606190.000000 = [x]
r= 31443101 0.000000 = m*(x-[x])
但是当注释掉的行(即,//如果(argc>1)r=atol(argv[1]);
)未注释时,它会生成:
j= 20000 9007199616606190.000000 = x
k= 17285 9007199616606190.000000 = [x]
r= 31443101 0.000000 = m*(x-[x])
即使该行应该没有效果,因为argc>1
为false。有人对这个问题有合理的解释吗?它在任何其他系统上都是可复制的吗
#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;
}
我还没有找出差异的原因。在我的系统上没有重复,Win7运行CygWin和GCC4.3.4。无论是否使用
if
语句,j
的值都设置为零,而不是20K
我唯一的建议是使用gcc-S
查看汇编程序的输出。这应该能告诉你出了什么问题
具体来说,将汇编程序输出生成两个单独的文件,一个用于工作变量,一个用于非工作变量,然后vgrep(并排观察它们)以尝试确定差异
顺便说一下,这在您的环境中是一个严重的失败。当
m
为10000时,这意味着x-楼层(x)
必须等于2。在我的一生中,我想不出任何实数,在这种情况下:-)取消注释该行可能会影响结果的原因是,如果没有该行,编译器可以看到r
和jroot
在初始化后无法更改,因此,它可以在编译时而不是运行时计算x
。当行未注释时,r
可能会更改,因此必须将x
的计算推迟到运行时,这可能导致以不同的精度进行计算(特别是在使用387浮点数学的情况下)
您可以尝试使用-mfpmath=sse-march=native
来使用sse单位进行浮点计算,它不会显示过多的精度;或者您可以尝试使用-ffloat store
开关
您的减法
x-floor(x)
显示出灾难性的取消-这是问题的根本原因,需要避免;) 我认为有两个原因可以解释为什么这条线会产生影响:
- 如果没有这一行,所有这些变量的值都可以(而且,IMHO,很可能是)在编译时确定;使用该行,必须在运行时执行计算。但是很明显,编译器的预计算值应该与运行时计算的值相同,我倾向于认为这是观察到的不同行为的实际原因。(不过,它肯定会在汇编程序输出中显示出巨大的差异!)
- 在许多机器上,浮点运算是使用中间值中比实际存储在双精度浮点数中更多的位来执行的。您的第二个版本通过创建两个不同的代码路径来设置
,基本上将x
限制为可以存储在双精度浮点数中的内容,而您的第一个版本允许x
的初始计算值仍然可以作为中间值使用,并带有额外的位,计算后续值时。(无论这些值是在编译时还是在运行时计算的,都可能是这种情况。)x
使用优化确实会根据您的编辑更改您的结果,因此这很可能是gcc问题,或者是您的代码和gcc的组合(而不是硬件fpu问题)。然后在同一台计算机上尝试不同版本的gcc。例如,4.4.x而不是4.5.x。该代码在我的Linux和OS x系统上产生相同的输出——无论该行是否被注释掉。对我来说不可复制;
j=0
和k=0
在有注释行和没有注释行的情况下运行。Ubuntu 11.04,gcc版本4.5.2(Ubuntu/Linaro 4.5.2-8ubuntu4)
,英特尔核心i7 920(系列6,型号26,步进4)。(64位操作系统和用户区。)我在我的Mac OS机器上都得到了前一个结果。在4.0到4.6的几个不同版本的gcc中。看起来这可能是您的情况所特有的。我无法用完全相同版本的gcc
重现这个问题,尽管gcc本身编译为x86-64二进制(当然,使用-m32
编译32位目标)。@jwpat7,您应该显示这两种情况下的所有汇编代码。我区分了文件,区别在于:(1)使用if
in时,多了8个堆栈访问偏移量,以及(2)atol()的10行代码呼叫,通过一个jle.L2
跳转。我要在我的ot上试用该代码
... ;; 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
...