C 如何确保不同系统上的浮点数相同?

C 如何确保不同系统上的浮点数相同?,c,floating-point,platform-independent,C,Floating Point,Platform Independent,如果我在windows和linux(ubuntu)上编译下面的c代码行,我会得到不同的结果。我想避免。我怎么做 double a = DBL_EPSILON; double b = sqrt(a); printf("eps = %.20e\tsqrt(eps) = %.20e\n", a, b); linux输出: eps = 2.22044604925031308085e-16 sqrt(eps) = 1.49011611938476562500e-08 windows

如果我在windows和linux(ubuntu)上编译下面的c代码行,我会得到不同的结果。我想避免。我怎么做

 double a = DBL_EPSILON;
 double b = sqrt(a);
 printf("eps = %.20e\tsqrt(eps) = %.20e\n", a, b);
linux输出:

eps = 2.22044604925031308085e-16        sqrt(eps) = 1.49011611938476562500e-08
windows输出:

eps = 2.22044604925031310000e-016       sqrt(eps) = 1.49011611938476560000e-008
在linux上使用gcc和clang在32位和64位系统上进行测试,结果相同。
在使用32位gcc mingw和32位和64位visual studio测试的windows上,结果也相同。

在您给出的示例中,两个程序的浮点数似乎都相同。他们只是以不同的方式打印。围绕这个问题最简单的解决方案是编写自己的浮点打印函数。如果您不希望得到太好的输出,可以将该函数用作伪代码,用C编写自己的代码。它不是正确的四舍五入,但它适用于它的预期用途(即,可复制和可读的输出)


您的问题提示您遇到的一个更深层次的问题是浮点计算在不同的平台上给出不同的结果。这是因为C标准没有强制编译器精确地实现IEEE 754浮点标准,特别是允许中间结果具有更高的精度。C标准的这种相对宽松至少部分是由历史上的x86浮点指令造成的,这使得实现精确的IEEE 754语义非常昂贵

在Linux上,假设您使用的是GCC,请尝试
-msse2
编译选项编辑:OP评论说
-msse2-mfpmath=sse
为他工作。
这使得GCC生成现代SSE2指令,给出准确的IEEE 754浮点语义。如果在Windows上也使用GCC,请使用相同的选项

如果您使用的是Visual C:Visual C使用另一个技巧强制历史浮点指令与IEEE 754语义匹配:它告诉旧的80位浮点硬件仅使用IEEE 754双精度所具有的有效位。这提供了双精度数字的精确模拟,除了一些您不会遇到的拐角情况。在这种情况下,如果您的程序只使用双精度数字(C
double
类型),则会有帮助(*)


(*)从理论上讲,Visual C编译器可以通过将每个中间结果从双精度舍入到单精度来生成计算精确单精度算术的代码,但这将非常昂贵,我怀疑它是否能做到这一点

每个系统的底层架构是什么?使用的c库的
printf
实现不同。Windows one仅打印17位左右的有效数字,如果需要更多,则用零填充。glibc的打印出正确的舍入值。但对我来说,这似乎不仅仅是打印的问题,我的数值算法也会返回稍微不同的结果。我指出了这几行,因为我假设它们是差异的来源。#
double
变量通常具有较高的精度。打印或比较超过16的数字是没有意义的。@blastfull:打印超过16的数字不是没有意义的。它可能需要54位十进制数字来表示64位IEEE 754二进制浮点数的精确值,人们可能出于各种原因希望看到精确值,例如将值传输到能够处理更高精度的数学软件或进行调试。我不知道是否会调用溢出和下溢“您不会遇到的角落案例"; 它们在真实代码中经常出人意料地出现。@StephenCanon如果你抓住了我,我可能并不完全诚实:可能会发生下溢和溢出。我的问题是,如果它们真的发生了,除了手动插入程序之外,我没有任何解决方案可以提供,无论它们发生在哪里,
intermediate\u result=intermediate\u result>=0x1.0p+1024?inf:中间结果。对于underflow,情况更糟,我没有看到任何可移植的代码来截断有效位。如果程序员知道他正在使用x87,那么对
ldexp()
的两次调用应该可以做到这一点,但这会将IEEE 754上的非规范化刷新为零hardware@StephenCanon此外,“对ldexp()的两次调用”`解决方案引入了双舍入。是的,这是一个非常棘手的问题;让我们希望提问者能够幸运地避免它。似乎对于gcc“-msse2-mfpmath=sse”适用于我的问题。谢谢!