首先,这不是关于精度或类似问题的问题。
我的问题是,编译器如何决定如何表示数字?
我们以C为例。我写了
double d = 4.5632;
如何选择二进制表示?我知道它没有完全代表,所以它如何选择最接近的可表示数字?它是在编译时完成的吗?它是由CPU还是操作系统完成的?
请仅在您知道如何发生这种情况时才回答,“不要担心”这样的答案没有帮助。此外,“它取决于平台”也没有帮助,你可以选择一个平台并为此解释。
答案 0 :(得分:6)
编译器不会(通常)决定。 CPU(通常)具有浮点单元,该浮点单元要求浮点值以特定格式表示(通常为IEEE-754)。当然,可以模拟完全不同的体系结构,在这种情况下,编译器/模拟器作者可以自由选择完全不同的表示。但这不典型。
关于如何将特定词汇表示4.5632
转换为基础表示,这是由C标准指定的。因此,从C99标准的第6.4.4.2节(我已经强调了最相关的部分):
有效位数部分被解释为(十进制或十六进制) 有理数;指数部分中的数字序列是 解释为十进制整数。对于十进制浮点常数, exponent表示有效位数部分的幂10 缩放。对于十六进制浮点常量,指数表示 2的幂,有效位部分将被缩放。对于 十进制浮点常量,也用于十六进制浮点数 当FLT_RADIX不是2的幂时,常量结果是 最接近的可表示值,或者更大或更小的可表示值 值紧邻最近的可表示值,选择 以实现定义的方式。对于十六进制浮动 常量当FLT_RADIX是2的幂时,结果是正确的 圆形。
这将在编译时完成(虽然标准没有强制要求)。
答案 1 :(得分:0)
是的,特定转换是在编译时完成的,因为double d = 4.5632;
是编译时常量。编译到代码中的是以目标体系结构使用的浮点格式表示此值。在32位IEEE-754表示的情况下,这是0x409205BC
。 CPU如何“知道”这是一个接近4.5632的值取决于浮点标准本身。同样,在32位IEEE-754的情况下,我们有一位用于符号,八位用于指数,23位用于尾数。
在舍入时,有几种方法可以应用。 IEEE-754规范提到了四种方法:舍入到最接近,舍入到零,舍入到负无穷大,舍入到正无穷大。
答案 2 :(得分:0)
编译器生成一个在平台上运行的程序。该平台可能在编译器之前存在,反之亦然。所有内容的二进制表示构成了ABI,它本质上是编译器输出的规范。最后,无论出于何种原因,事情已经完成,但希望有一个ABI来确切说明发生了什么。
实际上,几乎所有平台都根据IEEE 754(即IEC 559)实现浮点运算。这个相当古老的国际标准定义了浮点数的位是什么意思,以及程序十进制表示应该如何舍入到一个浮点值。
没有FPU的平台通常仍会在IEEE 754号码的软件中打包和解包位域,因为它们很可能以二进制形式出现在文件中。
对互操作性和数字精度要求有限的平台(如GPU)可能会放宽IEEE 754要求的精度标准,但它定义的数值范围最适合各种应用。
当然,如果你想要最终的便携性,你不能依赖任何东西。但是可以肯定的是,在编译时执行从十进制FP到二进制FP的转换(假设FPU本身不是十进制)。
答案 3 :(得分:0)
对于您的具体示例,是二进制表示在编译时进行编码。它可能会调用一个C库(atod,sscanf,etC),无论该库执行截断还是舍入,都会发生这种情况。而编译器“功能”或“规则”的功能并不一定与执行相同操作时发生的运行时规则相同。你永远不应该检查浮点的等价性,但是如果你要获取编译时的值,然后为程序提供一个字符串并转换该运行时(比如你在命令行上传递值4.5632并使用其中一个库调用)你不一定会获得相同的浮点值。我已经看到编译器(gcc等)使用编译时常量做了一个非常糟糕的工作,所以作为规则,对于像你这样的数字(在尾数中不多),我对准确性的偏好是这样做的:
double d; int a;
a 45632;
d = a;
d/=10000;
即使它优化了,也往往会得到更好,更准确的答案。
你确实在int转换为双转换时存在硬件+操作系统错误的风险,Hauser对FPU错误倾向于在int中浮动和浮动到int操作做了一些评论。即使在编译时,我也会假设编译器实际上会对浮点数执行两次int,然后执行除法,而不是像代码那样直接浮动一个字符串。
自从我展示了所有这些之后已经有几年了,也许编译器变得更好(可疑)。希望硬件变得更好(可能,在过去很容易找到错误的情况下找到fpu非常罕见)。
答案 4 :(得分:0)