float 精度_对一个 float 精度问题的分析

本文详细分析了浮点数在计算机中的表示和运算过程,以123456.789e5和20为例,探讨了浮点数的存储格式(IEEE754单精度)、阶码对齐、尾数相加等步骤,并讨论了精度损失问题。同时列举了浮点数的特殊值,如正负零、无穷大和NaN的二进制表示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原问题见:

有1个c语音的源代码不清楚它为什么输出这个数,所以想请教一下?​www.zhihu.com
ae158c209b058f0bfded309383c65156.png

大概说一下问题,对于程序:

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. 当 (-1)^s 为 1 时,表示正数,为 -1 时,表示负数,在计算机内存储时,0 表示正数,1 表示负数
  2. M 表示尾码,范围为 [1, 2),可以写成 1.xxxxx 的形式,由于, 1 肯定存在,所以,在计算机内只存储小数部分
  3. 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)

结论:浮点数,进行存储或运算时,可能会有精度损失

此外,还有几个特殊的浮点数:

  1. 符号位,指数部分,尾数部分都为 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值