![16d374e358334a88695ac63029d0e5f9.png](https://img-blog.csdnimg.cn/img_convert/16d374e358334a88695ac63029d0e5f9.png)
今天我们聊一聊浮点数。虽然编程过程中经常使用浮点数,但是你真的了解浮点数吗?
浮点数
首先几个问题:
1、为什么叫浮点数?
2、为什么float和int同样是32位,但是float的取值范围为-3.40E+38 ~ +3.40E+38而int的取值范围为-2147483648~2147483647
3、浮点数为什么计算时经常“出错”?
注:以下没有特殊说明的情况下,浮点数均表示单精度浮点数
1、为什么叫浮点数
数学上,我们都叫小数,为什么计算机里叫浮点数而不直接叫小数。我来解释一下这个词。这个词应该分成三部分解释--“浮”、“点”、“数”。
从后向前解释,“数”就不多解释了,是个抽象的概念,就当数字理解吧。“点”,这里说的是小数点,而“浮”,是浮动的意思。合起来就是浮动的小数点的数字。
2、为什么浮点数的范围会这么大
今天我们只聊一下单精度浮点数,也就是我们常用的float。当然还有双精度的浮点数,即double,二者原理相同,只不过double的位数更多一点。
单精度浮点数一共32位(bit),其中最高1位是符号位(Sign),正数为0,负数为1;然后的8位是指数(Exponent)位,最后的23位保存的是有效的数字(Fraction)。转换公式为
±M × 2E
M为有效数字,E是指数,这就是IEEE(电气和电子工程师协会)的标准(754)。
我们举个例子,如下图(我用Excel画的,哈哈):
![8ae3adfc9cde75239cd877eb66f458d6.png](https://img-blog.csdnimg.cn/img_convert/8ae3adfc9cde75239cd877eb66f458d6.png)
我们算一下这个数字表示的值是多少。
首先符号位是0,所以这个数是个正数“+”。
指数部分是01111100,也就是十进制的124,这里需要再减去127。为什么?因为指数部分正好是8位,表示的数字范围为0~255,0~126表示负数,127表示0,128~255表示正数,为什么?IEEE的标准。所以,指数位表示的是124-127 = -3。
有效数字需要再说明一下,虽然有23位,但实际上是有24位的。为什么?小数点最右边的1并不会储存,因为它一定存在(二进制的第一个有效数字必定是1)。换言之,有效数位是24位,实际储存23位。所以上面的有效数字表示为
1×20 + 0×2-1+ 1×2-2 = 1.25。
最终值,套入公式
+ 1.25×2-3 = 0.15625
所以上面的值表示的是0.15625。
那么,浮点数的范围是怎么算出来的呢?请往下看。
3、浮点数为什么很容易“出错”
说这个问题,先看一段代码。
![0c70cb395c7348494327e30eb1da79f8.png](https://img-blog.csdnimg.cn/img_convert/0c70cb395c7348494327e30eb1da79f8.png)
我们预期的结果应该是9.9,为什么是9.90001?这就是浮点数最“坑”人的地方。
浮点数不管怎么保存,终究还是二进制数,2-1 是0.5,2-2 是0.25,2-3 是0.125,2-4 是0.0625,2-5 是0.03125...所以,除了这些值和这些值任意组合加减的值是能精确表示的,其他的值都是“近似值”,所以明白了为什么计算机计算小数的时候,会“出错”的原因了吧?而且,浮点数不是什么时候都有“误差”。
4、0.0f/0.0f是什么
等等,数学老师说,被除数不能为0,而且0除0的时候,执行的时候不会抛异常吗?
我们先看一下C#源码里的几行代码
![825c2cf722a2198e98300c05bed44a0e.png](https://img-blog.csdnimg.cn/img_convert/825c2cf722a2198e98300c05bed44a0e.png)
也就是说,正无穷是1.0/0.0,负无穷是-1.0/0.0,0.0/0.0是NaN(Not a Number)。
当E(Exponent,后面缩写相同含义)位为0,并且F(Fraction,后面缩写相同含义)位也为0的时候,此时浮点数受S(Sign,后面缩写相同含义)位的影响也就出现了+0和-0的两种情况。当E位为全1且尾数F位为全0时,表示为无穷大,结合符号位S位为0或1,也有+∞和-∞之分。这样在32位浮点数表示中,要除去E位用全0和全1表示零和无穷大的特殊情况,指数的偏移值不选128而选127。当F位的值不为0时,尾数域的最高有效位应为1,否则以修改阶码同时左右移小数点的办法,使其变成这一表示形式,这称为浮点数的规格化表示。对于规格化浮点数,E位的范围变为1到254,真正的指数值E则为-126到+127。即如果E位为0,而且同时F位不为0的时候,浮点数为±F×2-126 。因此,E位的表示范围为1~254,想想浮点数的取值范围,-2126 ~ 2127 ,明白了吧?
当E位为255的时候,并且F位不为0,表示的就是非法数字。因为超过了浮点数的取值范围。
所以所有的浮点数都是用0x7F800000来表示正无穷大,0xFF800000表示负无穷大。
![ce7705d02fc3898b18779fd365624a0c.png](https://img-blog.csdnimg.cn/img_convert/ce7705d02fc3898b18779fd365624a0c.png)
对于为什么除0.0不会抛异常,你需要了解一下浮点数做除法的原理,这里要是展开的话内容非常之多。不过Java里有一个函数Float.intBitsToFloat(int bits),我们可以尝试一下
![ac566735ff77bce2d54a1609bdb5a263.png](https://img-blog.csdnimg.cn/img_convert/ac566735ff77bce2d54a1609bdb5a263.png)
你会发现,控制台会输出equality。对于网上有写人说1.0/0.0是因为0.0不是真正的0.0,只是近似的0.0这种说法,请回头看上面的问题3。
最后说一句,因为float、double都不会在除以0的时候抛异常,因此,写代码的时候更要格外小心,被除数为0的时候,是不是你想要的结果呢?