原问题见:
有1个c语音的源代码不清楚它为什么输出这个数,所以想请教一下?www.zhihu.com
大概说一下问题,对于程序:
float a = 123456.789e5;
float b = a + 20;
printf("%fn", b);
将输出什么?是12345678920.000000 吗?
下面仅考虑 float 为使用 IEEE 754 32 位单精度浮点数的情况,
即 float 为 32 位,其中 1 为符号位,接下来,8 位阶码,最后,23 位尾码
任何一个浮点数(除 0 以外),都可以表示为:
V = (-1)^s * M * 2^E
- 当 (-1)^s 为 1 时,表示正数,为 -1 时,表示负数,在计算机内存储时,0 表示正数,1 表示负数
- M 表示尾码,范围为 [1, 2),可以写成 1.xxxxx 的形式,由于, 1 肯定存在,所以,在计算机内只存储小数部分
- E 表示阶码,阶码可正可负,在计算机内存储时,会将实际的阶码加一个偏移量存储,对于 32 为 float,偏移量为 127
接下来分析这个数:
123456.789e5
= 12345678900
= 8589934592 + 2147483648 + 1073741824 + 268435456 + 134217728 + 67108864 + 33554432 + 16777216 + 8388608 + 4194304 + 1048576 + 524288 + 262144 + 4096 + 2048 + 1024 + 32 + 16 + 4
= 2^33 + 2^31 + 2^30 +2^28 + 2^27 + 2^26 + 2^25+2^24+2^23 + 2^22 + 2^20 + 2^19 + 2^18 + 2^12+2^11 + 2^10 + 2^5 + 2^4 + 2^2
= 10 1101 1111 1101 1100 0001 1100 0011 0100(二进制)
= 1.0 1101 1111 1101 1100 0001 1100 0011 0100 * 2^33 (二进制)
此数大于 0,所以符号位部分存储为 0
此数,阶码为33,偏移量为 127,33 + 127 = 160 = 1010 0000,
阶码部分存储为 1010 0000
如上所述,对于 1.0 1101 1111 1101 1100 0001 1100 0011 0100,最前面的 1 不存储,只存储小数位,由于,尾码只有 23 为,而 1.0 1101 1111 1101 1100 0001 1100 0011 0100,小数部分有 33 位,所以只能忽略点最后 10 位,即,尾码部分为 011 0111 1111 0111 0000 0111(此处损失了精度)
最后此数在计算机内表示为:0101 0000 0011 0111 1111 0111 0000 0111
将其转化为浮点数为:12345678848.000000,所以,float 类型的 123456.789e5 在计算机内表示为:12345678848.000000
接下来,在考虑 a + 20,int 和 float 相加,int 将先转换为 float
所以,20 = 2^4 + 2^2 = 1.01 * 2 ^ 4,即:
符号位表示为:0
阶码为 4,加上偏移量 127,4 + 127 = 131 = 1000 0011,阶码部分存储为 1000 0011
尾码,对于 1.01 不存储最前面的 1,阶码共 23 位 ,而小数部分只有两位,不足用 0 填,所以,尾码,存储为:010 0000 0000 0000 0000 0000
所以 float 类型的 20,存储为 0100 0001 1010 0000 0000 0000 0000 0000
现在,再考虑两数相加,
0101 0000 0011 0111 1111 0111 0000 0111(123456.789e5)
0100 0001 1010 0000 0000 0000 0000 0000(20)
首先,进行,阶码对齐,123456.789e5 的阶码为:1010 0000(160),20 的阶码为 1000 0011(130),
小阶码向大阶码对齐,所以,将 0100 0001 1010 0000 0000 0000 0000 0000(20)的阶码转化为 1010 0000 (160),将尾码(包括未存储的 1)向右移 30 位,空位用 0 填充, 即转换为 0101 0000 0(0)000 0000 0000 0000 0000 0000(此处有精度损失)
现在:
0101 0000 0(1)011 0111 1111 0111 0000 0111(123456.789e5)
0101 0000 0(0)000 0000 0000 0000 0000 0000(20)
接下来,阶码不变,由于两数都是正数,所以尾码相加(需要加上尾码未存储的1),尾码部分结果为:(1)011 0111 1111 0111 0000 0111
然后,进行格式化,这个例子不需要格式化,(此步可能损失精度)
所以,加上符号位和阶码,最终结果为:
0101 0000 0011 0111 1111 0111 0000 0111(12345678848.000000)
结论:浮点数,进行存储或运算时,可能会有精度损失
此外,还有几个特殊的浮点数:
- 符号位,指数部分,尾数部分都为 0,表示 0.0
0000 0000 0000 0000 0000 0000 0000 0000
2. 符号位为 1,指数部分,尾数部分都为 0,表示 -0.0
1000 0000 0000 0000 0000 0000 0000 0000
3. 符号位为 0,指数部分都为 1,尾数部分都为 0,表示正无穷大
0111 1111 1000 0000 0000 0000 0000 0000
4. 符号位为 1,指数部分都为 1,尾数部分都为 0,表示负无穷大
1111 1111 1000 0000 0000 0000 0000 0000
5. 符号位无所谓,指数部分都为 1,尾数部分为非 0,表示不是一个数字,是 NaN
*111 1111 1 + 非0