使用 gcc 4.8.2 (Ubuntu 14.04) 我得到了不同的结果,但基本上以相同的方式计算一个值。根据我测试的系统上的架构(32 位/64 位),也存在差异。
#include <math.h>
#include <stdio.h>
int main()
{
float h = 0.11f;
float y = 0.11f;
float g = 1.37906f;
float x = 2.916949f;
float result1 = (h * y / fabs(g)) / x;
float result2 = h * y / fabs(g);
result2 /= x;
float result3 = (h * y / g) / x;
printf("%.20f \n", result1); //0.00300796888768672943
printf("%.20f \n", result2); //0.00300796912051737309
printf("%.20f \n", result3); //0.00300796912051737309 on x64
//0.00300796888768672943 on x32
}
这是什么原因,我该如何预测或避免这些差异?
编辑:将晶圆厂转换为 float 不会改变结果,至少在我的系统上是这样(请参阅 Oli Charlesworth 的评论)。
最佳答案
C99 标准并没有像早期的 Java 标准那样强制所有 C 编译器为浮点计算实现严格的标准,而是允许在理想模式方面有一些变化,在这种理想模式下,每个操作都按照 IEEE 的顺序和舍入进行754格式对应浮点型。
您可以询问 GCC 它遵循的是什么浮点计算模型,您可以使用命令行选项来更改其行为并使其更易于预测。 有两种情况:
- 如果您要生成 387 代码,请使用最新的 GCC(4.8 应该没问题)和
-std=c99
进行编译。没有它(具体来说,没有它暗示的-fexcess-precision=standard
),浮点计算的确切结果是不可预测的,并且您允许编译器为result1 产生不同的结果
、result2
和result3
)。对于-std=c99
,result1
和result3
的值必须相同。result2
的值可能不同,因为对result2
的中间赋值会强制将计算点的值四舍五入为float
。 - 停止生成 387 代码,改为生成 SSE2 代码(选项
-msse2 -mfpmath=sse
)。在此模式下,fabs
已替换为fabsf
的所有三个计算都应产生相同的结果。这样做的缺点是生成的代码仅与过去 12 年左右生产的处理器兼容(!)
关于c - 同样简单的计算,不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24110539/