将INT_MAX转换为浮点数,然后再转换回整数。

10
在C编程中,我遇到了一个奇怪的问题,与我的直觉相反。当我声明一个整数变量作为INT_MAX(在limits.h中定义为2147483647),并将其隐式转换为浮点数时,它可以正常工作,即浮点数值与最大整数相同。然后,我将浮点数再次转换回整数,一些有趣的事情发生了。新的整数成为了最小整数(-2147483648)。以下是源代码:
int a = INT_MAX;
float b = a; // b is correct
int a_new = b; // a_new becomes INT_MIN

我不确定当浮点数b转换为整数a_new时会发生什么。那么,有没有合理的解决方案来找到可以在整数浮点数类型之间切换的最大值?

PS:值INT_MAX - 100可以正常工作,但这只是一种任意的解决方法。


浮点数只有有限的精度,所以我认为这种转换并不奇怪。 - ymonad
1
与您的评论相反,b不正确的。如果您仔细查看,您会发现它实际上是在第一次转换后的INT_MAX+1。在您的平台上,INT_MAX为2147483647,而不是2147483648。也就是说,第一次转换是引入结果增量的地方。在线查看 - WhozCraig
1
一个4字节的浮点数使用23位来存储尾数,9位来存储符号和指数。这意味着无法完全准确地存储最大的32位整数。 - Jonathan Leffler
看起来问题并不会在所有平台上发生:请参见此处示例 - Aurélien Gasser
2
@AurélienGasser 这是一个谬论。我相信编译器默认会进行优化。禁用优化,然后查看结果。所涉及的数字无法准确表示为浮点数,因此无法将其恢复。 - devnull
1个回答

16
此答案假设 float 是以32位编码的IEEE-754单精度浮点数,并且一个 int 是32位的。有关IEEE-754的更多信息,请参见此维基百科文章
浮点数只有24位精度,而 int 有32位。因此,从0到16777215的 int 值在浮点数中具有精确表示,但大于16777215的数字不一定具有浮点数的精确表示。以下代码演示了这一事实(在使用IEEE-754的系统上)。
for ( int a = 16777210; a < 16777224; a++ )
{
    float b = a;
    int c = b;
    printf( "a=%d c=%d b=0x%08x\n", a, c, *((int*)&b) );
}

期望的输出是

a=16777210 c=16777210 b=0x4b7ffffa
a=16777211 c=16777211 b=0x4b7ffffb
a=16777212 c=16777212 b=0x4b7ffffc
a=16777213 c=16777213 b=0x4b7ffffd
a=16777214 c=16777214 b=0x4b7ffffe
a=16777215 c=16777215 b=0x4b7fffff
a=16777216 c=16777216 b=0x4b800000
a=16777217 c=16777216 b=0x4b800000
a=16777218 c=16777218 b=0x4b800001
a=16777219 c=16777220 b=0x4b800002
a=16777220 c=16777220 b=0x4b800002
a=16777221 c=16777220 b=0x4b800002
a=16777222 c=16777222 b=0x4b800003
a=16777223 c=16777224 b=0x4b800004

有趣的是,float值0x4b800002用于表示三个int值16777219、16777220和16777221,因此将16777219转换为float再转换回int不会保留int的精确值。


INT_MAX最近的两个浮点数值分别为2147483520和2147483648,可以通过以下代码演示:

for ( int a = 2147483520; a < 2147483647; a++ )
{
    float b = a;
    int c = b;
    printf( "a=%d c=%d b=0x%08x\n", a, c, *((int*)&b) );
}

输出中有趣的部分是

a=2147483520 c=2147483520 b=0x4effffff
a=2147483521 c=2147483520 b=0x4effffff
...
a=2147483582 c=2147483520 b=0x4effffff
a=2147483583 c=2147483520 b=0x4effffff
a=2147483584 c=-2147483648 b=0x4f000000
a=2147483585 c=-2147483648 b=0x4f000000
...
a=2147483645 c=-2147483648 b=0x4f000000
a=2147483646 c=-2147483648 b=0x4f000000
请注意,从2147483584到2147483647的所有32位值将会被舍入到2147483648的值。最大的向下舍入的值是2147483583,这与32位系统上的(INT_MAX - 64)相同。
因此,有人可能得出结论,低于(INT_MAX - 64)的数字将安全地从转换为,并再次转换回。但这仅适用于int大小为32位,并且按IEEE-754进行编码的系统。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接