转载自https://airguanz.github.io/2019/11/20/float-to-int-exact-range.html
我们知道,IEEE 754中的浮点数是对实数的近似表示,其取值密度在越远离0的地方越稀疏。那么,究竟在多远处浮点数会无法精确地cast到整数呢?本文对此进行了实验和分析。
实验代码很简单:
int main()
{
for(int i = 0; i < std::numeric_limits<int>::max(); ++i)
{
float f = static_cast<float>(i);
if(i != static_cast<int>(f))
{
cout << i << endl;
break;
}
}
for(int i = 0; i > std::numeric_limits<int>::min(); --i)
{
float f = static_cast<float>(i);
if(i != static_cast<int>(f))
{
cout << i << endl;
break;
}
}
}
这段代码在VS2019
中的输出如下:
16777217
-16777217
也就是说,从+-16777217开始,我们就无法保证一个int
被转为float
后,还能完好无损地转回来了。对二进制数比较敏感的人可以注意到:
16777217=224+116777217=224+1
即浮点数能精确表示整数的范围为±224±224。这数字实在太“整”了,让我们试着从IEEE 754的浮点数格式中一探究竟。
注意到我们讨论的数值落在float
的规格化表示范围内。float
包含1个符号位,8个指数位,以及23位的尾数。设符号位为ss,阶码的无符号整数解释为ee,尾数的位表示为
f1f2…f23f1f2…f23
则规格化的浮点数取值为:
(−1)s×(1+2−1f1+2−2f2+⋯+2−23f23)×2e−127(−1)s×(1+2−1f1+2−2f2+⋯+2−23f23)×2e−127
当e−127=23e−127=23时,上式变为:
(−1)s×(223+222f1+221f2+⋯+2f22+f23)(−1)s×(223+222f1+221f2+⋯+2f22+f23)
此时根据ff每一位的值,该式可以取遍[−224+1,224−1][−224+1,224−1]中的每一个整数。而当e−127=24e−127=24且ff的所有位均为0时,±224±224也可以被精确地表示。一旦超过了这个范围,就出现了相邻的两个浮点值间的距离超过1的情况,也就无法再精确表示剩余的整数了。