C代码
#include <stdio.h>
int main(int argc, char *argv[]) {
double x = 1.0/3.0;
printf("%.32f\n", x);
return 0;
}
在Windows Server 2008 R2上,我使用MSVC 2013编译并运行代码。输出为:
0.33333333333333331000000000000000
在macOS High Sierra上,我使用Apple LLVM 9.0.0版(clang-900.0.39.2)编译并运行相同的代码。输出是:
0.33333333333333331482961625624739
在ideone.com上运行相同的代码会产生与macOS上相同的结果。
我知道双精度浮点格式只有大约15或16个有效十进制数字。该数字保留了macOS平台上64位二进制值1/3(作为Wikipedia's explanation)的精确十进制表示,但为什么它在Windows平台上被“截断”?
答案 0 :(得分:4)
根据C标准,以下行为是implementation-defined:
- 浮点运算(
醇>+
,-
,*
,/
)以及<math.h>
和{{中的库函数的准确性返回浮点结果的1}}是实现定义的,是<complex.h>
,<stdio.h>
中库函数执行的浮点内部表示和字符串表示之间转换的准确性。和<stdlib.h>
。实施可能表明准确性未知。
答案 1 :(得分:2)
从MSVC docs开始,因为MSVC符合IEEE double 最多 16 有效数字。
但the docs也声明默认情况下/fp
标志设置为精确:
使用/ fp:精确地在x86处理器上,编译器执行舍入 float类型的变量到赋值和的正确精度 强制转换以及何时将参数传递给函数。这四舍五入 保证数据不会保留任何大于的意义 其类型的能力。
因此,当你将它传递给printf
时,你将它变为圆形,得到那些零。据我所知,尾随1
是噪音,不应该在那里(它是数字17,我算了)。
此行为是MSVC
特定的,您可以在我链接的文档中详细了解它,以查看其他编译器需要哪些标记。
答案 2 :(得分:1)
696 当double被降级为float时,long double被降级为double或float,或者一个值以更高的精度表示 语义类型所需的范围(见6.3.1.8)是明确的 如果转换为它的语义类型(包括它自己的类型) 转换后的值可以用新类型表示 没有改变。
697 如果转换的值在可以表示但无法准确表示的值范围内,则结果为 选择的最接近的较高或最接近的较低的可表示值 以实现定义的方式。
698 如果转换的值超出了可以表示的值范围,则行为未定义。
答案 3 :(得分:1)
是的,不同的实现可能具有不同特征的double
类型,但这实际上并不是您在此处看到的行为的原因。在这种情况下,原因是用于将浮点值转换为十进制的Windows例程不会产生IEEE 754中定义的正确舍入结果(它们在有限数量的数字之后“放弃”),而macOS例程执行产生正确的舍入结果(事实上,如果允许产生足够的数字,则产生确切的结果)。
答案 4 :(得分:0)
简短的回答是:是的,不同的平台表现不同。
来自Intel:
Intel Xeon处理器E5-2600 V2产品系列支持半精度(16位)浮点数据类型。与单精度(32位)浮点数据格式相比,半精度浮点数据类型提供了2倍的紧凑数据表示,但牺牲了数据范围和精度。特别是,当32位浮点数据不适合L1缓存时,半浮点数可能比32位浮点数提供更好的性能。
我无法访问Xeon计算机来测试这个,但我希望你最终得到的double
不是真正的double
,因为使用了“更快的浮点,但精度更低的“CPU指令。
英特尔酷睿处理器的“i”系列没有半宽快速浮点精度,因此不会截断结果。
以下是英特尔网站上half-precision format in Xeon的更多信息。