域代码小数点_其实,我们都不懂代码(一)

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

我们算一下这个数字表示的值是多少。

首先符号位是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

我们预期的结果应该是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

也就是说,正无穷是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

对于为什么除0.0不会抛异常,你需要了解一下浮点数做除法的原理,这里要是展开的话内容非常之多。不过Java里有一个函数Float.intBitsToFloat(int bits),我们可以尝试一下

ac566735ff77bce2d54a1609bdb5a263.png

你会发现,控制台会输出equality。对于网上有写人说1.0/0.0是因为0.0不是真正的0.0,只是近似的0.0这种说法,请回头看上面的问题3。

最后说一句,因为float、double都不会在除以0的时候抛异常,因此,写代码的时候更要格外小心,被除数为0的时候,是不是你想要的结果呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值