为什么某些整数的双重转换(浮点型和返回整数)不能保持等于其原始数字,而有些却可以?

Why does the double conversion of some integers (to float and back to int) not remain equal to its original number, yet some do?

我有两个整型变量:

int i1 = 0xdeadbeefint i2 = 0xffffbeef.

(分别为11011110101011011011111011101111 or 37359285591111111111111111110111110111011111 or 4294950639)。


(int) (float) i1 == i1 的计算结果为假,而 (int) (float) i2 == i2 的计算结果为真。

这是为什么?在这个系统中,intsfloats都存储在4个字节中。

这是因为 float 的精度远低于 int,它无法存储所有可能的 int 值而不会使它们受到一些损坏。有时这种伤害只是四舍五入你的价值,有时你四舍五入的价值精确匹配。

A 32-bit float 只能存储 24 个“有效位”或数字数据。其他位保留用于指数、NaN 标记、无穷大等,这些会占用剩余存储空间 space.

double确实具有所需的精度,因为它通常是 64 位表示,可以存储 53 位数值数据数据。

正在进行大量转换。

int i1 = 0xdeadbeef; int i2 = 0xffffbeef 导致 实现定义的 转换,因为常量超出 int 范围。在这里,它们被“包裹”起来。

i2 是一个小值(15 个有效位),可以精确表示为 float.

i1 不是。 i1有30位有效位,比float的24位多了6位。那些较低的 6 不是 0,所以 (float) i1 结果是一个 舍入 值。

int main() {
  int i1 = 0xdeadbeef;
  int i2 = 0xffffbeef;
  printf("%d\n", (int) (float) i1 == i1);
  printf("%d\n", (int) (float) i2 == i2);
  printf("%u %10d %17f %10d\n", 0xdeadbeef, i1, (float) i1, (int) (float) i1);
  printf("%u %10d %17f %10d\n", 0xffffbeef, i2, (float) i2, (int) (float) i2);
}

输出

0
1
3735928559 -559038737 -559038720.000000 -559038720
4294950639     -16657     -16657.000000     -16657

C 实现通常使用 32 位 int,而 0xdeadbeef 不适合 32 位(一个符号位和 32 个值位)。使用 0xdeadbeef 初始化 i1 会导致转换为 int。此转换是实现定义的。例如,GCC 将其定义为对模 232 进行换行,这并不少见。

所以int i1 = 0xdeadbeef;i1初始化为deadbeef16 − 232 = 3735928559 − 232 = −559038737 = −2152411116。从“-21524111”中的 8 个十六进制数字可以看出,这个数字从其前导 1 位到尾随 1 位跨越 30 位(含 8 位中的 32 位,但前两位为零)。通常用于 float 的格式,IEEE-754 binary32,其尾数只有 24 位。任何有效位超过 24 位的数字都不适合该格式,并且在转换为此 float 格式时将被四舍五入。所以 i1 != (int) (float) i1.

相反,int i12 = 0xffffbeef;i2初始化为ffffbeef16 − 232 = 4294950639 − 232 = −16657 = −411116。这跨越 15 位(4 位数字中的 16 位,但第一个是零)。所以它适合float尾数的24位,转换为float时其值不变。所以 i2 == (int) (float) i2.