假如我们的浮点数:
float a=1.0;
在c++中,假设a占用四个字节,则该数可以由32位的0和1表示。具体表示如下:
0 | 01111111 | 00000000000000000000000 |
符号位 | 阶位 | 尾数 |
翻译一下:
符号位: 代表着该数值的正负
阶位: 阶位大小减去127后的值代表着小数点移动的位数
尾数:代表以1开头小数点后面的数字:1.00000000000......(23位)(这里注意以下,理论只有23位,但是协议要求在23位之前要放一个1,所以也可以理解有24位尾数,其中的第一位1是一个隐式位)
回到上面的例子:
0——正: 符号位为+
01111111——127:阶位为127
0000000....——1.000000....:1.000小数点后23个0
则移动的位数:127-127=0,故1.0000000...中的小数点不移动(浮动),然后将其转为10进制位+1.0;
再举个例子:0 10000101 11110110000000000000000
以上是个浮点数在内存中的表示:
第一步看符号位0,所以为正;
第二步看阶位10000101=133,所以浮动的位数位133-127=6;
第三步看尾数1.11110110000000000000000;
将其小数点浮动(移动6位)得到:1111101.10000000000000000,将其转为十进制:+125.5
综上:浮点数的浮点二字不是空穴来风,而且可以看出4字节的浮点数小数位最多有23位,转为十进制最多为7位,这也是为什么float数的精度只有7位的原因。32位浮点数表示范围介于-1.18X10+38和3.40X10+38之间,虽然浮点数表示的范围很广,但存储的尾数只有23位,因此它并不能精确表示其范围内的所有数字。
特殊bit:
浮点数转二进制
经典问题是0.1+0.2=?
首先先看0.1如何存储:
1. 0.1转为2进制
0.0001100… (重复1100)
2. 根据IEEE754可以得到下面的布局
所以可以看到其实0.1的实际浮点数是:0.100000001490116119384765625
注意
上面一开始说精度只有7位有效数字,结果0.1却可以表示出那么多,为啥? 与整数不同,因为浮点数在计算机中的存储方式(IEEE754),浮点数无法精确表示有效范围内的所有数值。有效范围内的数值是否可以被精确表示取决于有效数字M是否可以被小数域frac完全存储。例如3.5可以被精确存储,3.6无法被精确存储。
通常认为单精度浮点数的有效数字时6~7位,绝对可以保证的是6位。
说明:因为单精度浮点数使用23bit表示小数域,2的23次方是8,388,608。23位可以存储所有6位或更低的数字,以及大多数7位数字。
但是仅按6~7位有效数字使用浮点数,更保守的只使用6位的话,如果小数点后保留两位有效数字,那么整数位只能有4位有效数字,这其实是大打折扣的!
扩展
上面我们看了0.1本来翻译成十进制为1100重复,可是在最后一位bit却是1,如何按照截断的话,理论应该是0的,为什么是0呢?首先看看假如是0的话结果是多少:
0.0999999940395355224609
对比当最后一个bit是1的时候:
0.100000001490116119385
可以明显看到bit为1时,数字离0.1更近,那么有人会问,假如是0.10和0.11离0.1一样近的话如何取舍呢?结果是选择0.10,原因是选择最低有效数字为偶数,这是一种规则叫做:Round-to-Nearest-Even, 有时候也叫round-to-even.