浮点数的表示
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
这是 wiki 上的快速倒开方算法,非常神奇,但是阅读时发现
i = *(long*)&y
从这一句开始就开始有点懵逼了,由于个人能力的局限性,这里不打算深究整个算法的实现原理,而是打算从这一句代码去搞清楚计算机中的浮点数存储表示。
浮点数的二进制转换
float f = 0.15625;
int i = *(int*)&f;
printf("%d\n", i);
从这个例子出发,[1]首先定义了一个浮点数f = 0.15625,[2]中将f 的4个字节的内存地址转换成整型,[3]中将转换后的整型结果打印出来,结果是1042284544。
首先,单精度的具体定义可参考wiki,下面使用S表示符号位Sign,E 表示指数Exponent,M表示有效精度Significand precision。对于32bit 的浮点数而言,第31位表示符号位,30-23位表示指数,剩下的第23-0位表示有效精度。回到[1],这里的f = 0.15625,首先要转换成二进制:
0.15625 < 2 − 1 2^{-1} 2−1 = 0.5,所以第一位置0,得到0.0
0.15625 < 2 − 2 2^{-2} 2−2 = 0.25,第二位置0,得到0.00
0.15625 > 2 − 3 2^{-3} 2−3 = 0.125,第三位置1,得到0.001
0.15625 - 0.125 = 0.03125 < 2 − 4 2^{-4} 2−4 = 0.0625,第四位置0,得到0.0010
0.03125 = 2 − 5 2^{-5} 2−5,第五位置1,得到0.00101
所以 0.125 6 10 0.1256_{10} 0.125610 = 0.0010 1 2 0.00101_{2} 0.001012,整数部分按照正常二进制转换即可。
###浮点数表示
0.125
6
10
0.1256_{10}
0.125610 转换成二进制
0.0010
1
2
0.00101_{2}
0.001012 后,先转换成1.01 *
2
−
3
2^{-3}
2−3,指数位的表示因为是使用偏移值表示,所以这里需要加上偏移值127,即-3 + 127 = 124,二进制表示为
0111110
0
2
01111100_{2}
011111002,在浮点数的表示中,通常会省略最前面的1(需考虑其他情况时可以先参考这篇博客),有效数字为小数点后的数字,这里是01。
最后,回到最开始的存储表示:
0.15625为正数,符号位S 为0
指数位是124,二进制表示为 0111110 0 2 01111100_{2} 011111002,即E 为01111100
有效精度的数字表示为01,补足23位得到M为01000000000000000000000
最后得到内存中的二进制表示 00111110001000000000000000000000
结果
0.15625在内存中的的表示为00111110001000000000000000000000,当由[2]指定将这段内存的数据以整形的方式读取出来时,就会直接做二进制到十进制的变换,所以打印出来的就是1042284544。