为什么某些整数的双重转换(浮点型和返回整数)不能保持等于其原始数字,而有些却可以?
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 = 0xdeadbeef
和 int i2 = 0xffffbeef
.
(分别为11011110101011011011111011101111 or 37359285591
和111111111111111110111110111011111 or 4294950639
)。
(int) (float) i1 == i1
的计算结果为假,而 (int) (float) i2 == i2
的计算结果为真。
这是为什么?在这个系统中,ints
和floats
都存储在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
.
我有两个整型变量:
int i1 = 0xdeadbeef
和 int i2 = 0xffffbeef
.
(分别为11011110101011011011111011101111 or 37359285591
和111111111111111110111110111011111 or 4294950639
)。
(int) (float) i1 == i1
的计算结果为假,而 (int) (float) i2 == i2
的计算结果为真。
这是为什么?在这个系统中,ints
和floats
都存储在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
.