1. 首先看问题结果
是不是结果还是 30000000.0f 而不是 30000003.0f
2. 分析原因
这是一个典型的大数吃小数问题,原因需要从浮点数的计算机实现说起
对于float,是4字节(强调下我这里讲的都是32位浮点数,其它位思想同样适用),共32个bit,那么怎么用这32位来表示float
首先最容易想到的是 浮点数的每位十进制数用4bit来表示,比如 999999.99 这样每个9用1001表示,那么在32bit上的布局是下面这样: 100110011001100110011001.10011001 这样的表示数据范围是 000000.00 ~ 999999.99 共大概1亿个数字,这种把小数点固定的方法就是 “定点小数”,这样表达简单易懂,但是有个缺点就是,明明32bit可以最多表示42亿个数字,现在只能表示1亿,太浪费了。
还有一个就是 “浮点小数” ,顾名思义,就是小数点会浮动,比如 99.5 你可以表示成 9.995 x 10^1,也可以
表示成0.9995 x 10^2, 小数点可以浮动必然需要引入指数,这就是我们学数学的时候说的科学计数法,但是这是10进制,二进制数同样适用,只是科学计数法的底数是2而已。对于浮点数的二进制表示 常规的是 IEEE-754标准(强调下我下面讲都是IEEE-754标准),回头我打算再写一篇博客专门介绍一下这个标准(可以到时候留意下我的博客更新),现在我们只需要知道,这套标准定义下的浮点数是把32位bit位,划分为3部分:
对于 30000000.0f,二进制浮点数表示如下:
A = 0 10010111 11001001110000111000000 (依次是符号位1位 指数位8位 数字位23位)
对于 3.0f,二进制浮点数表示如下:
B = 0 10000000 10000000000000000000000 (依次是符号位1位 指数位8位 数字位23位)
转换工具可参考 : https://www.h-schmidt.net/FloatConverter/IEEE754.html
现在就是 A + B ,其中A和B的指数位和数字位均不同,CPU此时会把 A和B的指数位统一(向大数A的指数靠拢),这样只要 加 A和B的数字位就可以了,A和B的指数位相差23位,如果B的指数位要跟A一致,那么B的数字位就要整体向右移动23位,正好把B的数字部分
10000000000000000000000 第一个1溢出了,这就是为什么 30000000.0f + 3.0f 结果还是 30000000.0f的原因。
特别提醒下,IEEE754 的指数位表示的不是真实的指数值,而是有个映射关系:1~254(bit表示值,0和255有另外的作用) 映射到 -126~127(指数表示的真实值) 这 254 个有正有负的数上。如果我们把 32bit划分的3个部分分别表示为 s(符号位),e(指数位),f(数字位),那么没有参与映射的0和255有如下的作用:
下篇我在讲IEEE754标准的博客里会把这个也交代清楚