2147483645.0 的值将是 1.111111111111111111111111111101∙2³⁰
以二进制形式,所以它需要一个 30 位的尾数。但是float 数据类型只提供 23 位尾数,而 double 大约有 52 位。符号单独保存(这也取决于您的平台和编译器,此值适用于标准 x86)。
考虑这个程序:
#include <stdio.h>
int main() {
float x = -2147483645.0;
double y = -2147483645.0;
printf("%f %X\n", x, *((unsigned*) &x));
printf("%f %X%X\n", y, *( ((unsigned*) &y)+1), *((unsigned*) &y));
}
我用 gcc 5.4.0 for x86 编译它并得到输出:
-2147483648.000000 CF000000
-2147483645.000000 C1DFFFFFFF400000
十六进制数字的内部格式见右图:
float x (32 bits in total):
===========================
Sign: 1
Exponent: 100 1111 0 (bias 127 + 31)
Mantissa: 000 0000 0000 0000 0000 0000
double y (64 bits in total):
============================
Sign: 1
Exponent: 100 0001 1111 (bias 1023 + 32)
Mantissa: 1111 1111 1111 1111 1111 1111 1111 0100 0000 0000 0000 0000 0000
我在输出中对数字进行了分组。如上所述,双精度 y 精确存储数字的二进制表示。相反,浮点数x 的尾数为零。这是因为这些位不是简单地切断的。相反,该值会根据多余的位进行四舍五入。这就是为什么你在输出中得到1.0∙2³¹=2147483648。
您也可以在these 等网站上试用。
四舍五入由这里的 c 预处理器完成。我不知道如何影响这一点,但您可以在程序中控制舍入模式,如 here 所述:
#include <stdio.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
int main() {
float x;
double y = -2147483645.0;
fesetround(FE_TONEAREST);
x = y;
printf("FE_TONEAREST: %f %X\n", x, *((unsigned*) &x));
fesetround(FE_UPWARD);
x = y;
printf("FE_UPWARD: %f %X\n", x, *((unsigned*) &x));
fesetround(FE_DOWNWARD);
x = y;
printf("FE_DOWNWARD: %f %X\n", x, *((unsigned*) &x));
fesetround(FE_TOWARDZERO);
x = y;
printf("FE_TOWARDZERO: %f %X\n", x, *((unsigned*) &x));
}
使用-lm 选项编译。这输出
FE_TONEAREST: -2147483648.000000 CF000000
FE_UPWARD: -2147483520.000000 CEFFFFFF
FE_DOWNWARD: -2147483648.000000 CF000000
FE_TOWARDZERO: -2147483520.000000 CEFFFFFF