有没有办法确保浮点的输出在不同的操作系统中相同?
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 double
。 float
在传递给可变长度参数列表时被提升为 double
,因此第一个参数是可以的。但是第二个参数会导致 UB,除了幸运的是 double
和 long double
在您的平台上长度相同。
@ybungalobill 我的应用程序在 Windows 中运行,有很多数据,可能是百万行,写入文件。而且我将在 Linux 中运行相同的逻辑并写入文件,以检查 windows 中的日志是否正确。当我使用 svn diff 这些输出“不同”时,我会发现真正的不同,所以我想确保输出是一样的。
一个 printf 在平局的情况下四舍五入,超过的四舍五入与最接近的偶数(又名庄家四舍五入)。
尝试使用 fesetround() 将 Linux 端的浮点舍入模式设置为 FE_TONEAREST 或 FE_UPWARD。
@ybungalobill:%lf
与 printf
一起使用时等效于 %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】:
显而易见的答案是在输出中使用较少的精度。一般来说,
如果涉及任何计算,您甚至无法确定
实际浮点值是相同的。以及printf
和
ostream
round 是实现定义的,即使浮点数
值相等。
通常,C++ 不保证两个实现产生 相同的结果。在这种特殊情况下,如果很重要,您可以这样做 在进行转换之前手动舍入,但你仍然有 偶尔会出现问题,因为实际的浮点值将是 不同的。事实上,即使使用不同级别的 使用相同的编译器进行优化。所以你尝试的任何东西(除了 用汇编程序编写整个程序)注定是一场失败的战斗 到底。
【讨论】:
如问题所示,转换是从相同的二进制编码数字完成的......所以它适用于说printf()
-ing 在二进制网络数据包或文件中接收到的数字。在这种情况下,避免了舍入/计算/评估顺序/优化等问题。当然,问题可能会简化,这些问题可能仍然与“hdbean”相关,因此您的见解是有用的背景知识。以上是关于有没有办法确保浮点的输出在不同的操作系统中相同?的主要内容,如果未能解决你的问题,请参考以下文章