有没有办法确保浮点的输出在不同的操作系统中相同?

Posted

技术标签:

【中文标题】有没有办法确保浮点的输出在不同的操作系统中相同?【英文标题】:Is there any way to make sure the output of the float-point the same in different OS? 【发布时间】:2012-09-02 23:46:45 【问题描述】:

这是我的代码:

int a = 0x451998a0;
float b = *((float *)&a);
printf("coverto float: %f, %.10lf\n", b, b);

在 windows 中,输出为:

coverto float: 2457.539063, 2457.5390625000

在 linux 中的输出是:

coverto float: 2457.539062, 2457.5390625000

有什么方法可以确保输出是一样的吗?

【问题讨论】:

问:为什么需要它们相同?请注意,%f 打印出 double%lf 打印出long doublefloat 在传递给可变长度参数列表时被提升为 double,因此第一个参数是可以的。但是第二个参数会导致 UB,除了幸运的是 doublelong double 在您的平台上长度相同。 @ybungalobill 我的应用程序在 Windows 中运行,有很多数据,可能是百万行,写入文件。而且我将在 Linux 中运行相同的逻辑并写入文件,以检查 windows 中的日志是否正确。当我使用 svn diff 这些输出“不同”时,我会发现真正的不同,所以我想确保输出是一样的。 一个 printf 在平局的情况下四舍五入,超过的四舍五入与最接近的偶数(又名庄家四舍五入)。 尝试使用 fesetround() 将 Linux 端的浮点舍入模式设置为 FE_TONEAREST 或 FE_UPWARD。 @ybungalobill:%lfprintf 一起使用时等效于 %f。对于long double,您需要%Lf 【参考方案1】:

您看到的行为只是 Windows 的 printf() 函数的实现方式与 Linux 的 printf() 函数不同的结果。最有可能的区别在于printf() 实现数字舍入的方式。

printf() 在任一系统的底层如何工作是一个实现细节;因此,系统不太可能对printf() 如何显示浮点值提供如此细粒度的控制。

有两种方法可以使它们保持不变:

    在计算过程中使用比在显示过程中更高的精度。例如,一些科学和图形计算器使用double 精度进行所有内部计算,但仅以float 精度显示结果。

    使用跨平台的printf() 库。此类库很可能在所有平台上都具有相同的行为,因为确定要显示哪些数字所需的计算通常与平台无关。

但是,这确实没有您想象的那么大。输出之间的差异为 0.000001。这与这两个值的差异约为 0.0000000004%。显示错误真的可以忽略不计。

考虑一下:the distance between Los Angeles and New York is 2464 miles,它与显示输出中的数字具有相同的数量级。 0.000001 英里的差是 1.61 毫米。我们当然不会以接近那种精度的任何地方来测量城市之间的距离。 :-)

【讨论】:

@In silico 感谢您的回答。我只是好奇有什么方法可以确保输出相同。 @hdbean:您可以使用第 3 方库,以相同的方式在所有平台上实现浮点格式(假设 double 表示所有平台上的 ieee754 binary64)。 In silico:这没有抓住重点......有时获得精确匹配会简化回归测试(无论是在一个平台上还是跨平台)、运行时断言等。 @hdbean netlib.org/fp/dtoa.c 完全符合您的要求。 dtoa 函数将 double 转换为字符串,类似于标准 fcvt 函数,但以最小精确度和平台无关的方式进行。 @Tony Delroy:那么你做的回归测试是错误的。这样的测试应该检查实际的浮点值,而不是浮点值的显示。浮点值显示的回归测试由printf()的实现者编写,而不是printf()的用户编写。【参考方案2】:

如果您使用相同的 printf() 实现,它们很有可能会显示相同的输出。根据您的工作,在两种操作系统上使用 GNU GCC 可能会更容易,或者获取 printf() 源代码并将其添加到您的项目中(谷歌搜索应该没有问题)。

顺便说一句 - 你真的检查过那个十六进制数字编码吗?它应该向上还是向下取整? 625 本身很可能是四舍五入的,所以你不应该假设它应该四舍五入到 63....

【讨论】:

我们可以检查 Smalltalk,2457.5390625 asFraction -> (314565/128),分子只有 19 位,分母是 2 的幂 - 所以这是浮点数的确切值,因为 128 =2^7 小数后需要 7 位数字 2457.5390625 是准确的;因为它是一个精确的中途情况,它应该默认情况下(根据 IEEE-754)四舍五入到最接近的偶数:2457.539062。但是,这从未在 Windows 上正确处理过;出于某种未知原因,他们选择从零开始舍入,改为使用2457.539063。我不愿称其为错误,因为这是他们深思熟虑的选择。 @StephenCanon:你不能称它为错误,因为 C 标准仅在“推荐实践”部分要求正确舍入,即使这样也没有说“正确”意味着 IEEE-754。 【参考方案3】:

显而易见的答案是在输出中使用较少的精度。一般来说, 如果涉及任何计算,您甚至无法确定 实际浮点值是相同的。以及printfostream round 是实现定义的,即使浮点数 值相等。

通常,C++ 不保证两个实现产生 相同的结果。在这种特殊情况下,如果很重要,您可以这样做 在进行转换之前手动舍入,但你仍然有 偶尔会出现问题,因为实际的浮点值将是 不同的。事实上,即使使用不同级别的 使用相同的编译器进行优化。所以你尝试的任何东西(除了 用汇编程序编写整个程序)注定是一场失败的战斗 到底。

【讨论】:

如问题所示,转换是从相同的二进制编码数字完成的......所以它适用于说printf()-ing 在二进制网络数据包或文件中接收到的数字。在这种情况下,避免了舍入/计算/评估顺序/优化等问题。当然,问题可能会简化,这些问题可能仍然与“hdbean”相关,因此您的见解是有用的背景知识。

以上是关于有没有办法确保浮点的输出在不同的操作系统中相同?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法保证浮点计算的跨平台回归测试的结果?

如何确保在 windows 和 linux 上具有相同的浮点行为?

如何在Windows和Linux上确保相同的浮点行为?

如何确保两行不具有相同的值[重复]

如何确保 Caffe 分割网络输出大小与输入相同?

限制浮点精度?