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