为何二进制浮点数不准确?IEEE 754 二进制浮点数舍入规则
关注 1800
本节使用的“不准确”一词可能会被解释为“精度丢失”,如果从二进制的角度来讲,一些无限小数的数位的确丢失了,但从开发人员的角度看,数字更像是失去了准确性。
二进制浮点数不准确
二进制浮点数存在不准确的情况,尤其在计算的结果中,你可能会看到意想不到的数字。让我们来看一个经典的案例,在 JavaScript 中计算0.1与0.2的和,输出结果并不是理所当然的0.3,而是0.30000000000000004。
console.log(`0.1 + 0.2 = ${parseFloat('0.1') + parseFloat('0.2')}`)0.1 + 0.2 = 0.30000000000000004上面的示例说明,浮点数0.1,0.2,0.3,至少有一个是不准确的,当然,事实上他们每一个都无法准确。
二进制浮点数不准确的原因
二进制浮点数之所以会不准确,是因为我们习惯使用十进制,而十进制与二进制的数字并不是一一对应的,大部分十进制小数没有对应的二进制小数。
如果十进制数字只有整数部分,那么没有问题,你总能找到一个相同的二进制数字,只需要不断的加1或减1。
可一旦拥有小数部分,情况就变得完全不同了。如果把小数理解为对物体的划分,那么0.3相当于把物体分为 10 份然后取走其中的 3 份,这样的事情在二进制中是无法做到的,因为二进制的小数只能将物体分为 2 份,每一份都对应了十进制的0.5,虽然可以将 2 份划分为 4 份,甚至更进一步的划分下去,但最终你会发现,二进制没有等价于十进制0.3的小数,他只能无限的接近0.3,这也就是二进制浮点数不准确的原因。
最低位为 5 的十进制小数拥有对应的二进制小数
大部分十进制小数都找不到对应的二进制小数,除了以 5 结尾的十进制小数以外,比如十进制的0.5对应了二进制0.1。这可以被解释为,取出 10 份中的 5 份,与取出 2 份中的 1 份是等价的。
都怪人们不使用 0 和 1?!
当然,你也可以把不准确的原因,归于人们不习惯使用由0和1组成的二进制数字,一旦没有了十进制和二进制之间的转换,也就没有了不准确的问题。
IEEE 754 二进制浮点数舍入规则
由于存储空间有限,对于从十进制转换而来的二进制无限小数,IEEE 754 标准规定需要对其进行舍入,以便能够放入存储空间内。
IEEE 754 二进制浮点数就近舍入规则
IEEE 754 定义了四种舍入方式,默认采用就近舍入的方式,他类似于十进制的四舍六入五取偶。当被舍弃部分的最高位为0时不需要进1,这相当于四舍。当被舍弃部分的最高位为1并且后面不全为0时需要进1,这相当于六入。当被舍弃部分的最高位为1且后面全为0时,则根据未舍弃部分的最低位来决定是否进1,最低位为1,需要进1,最低位为0,不需要进1,这相当于五取偶。
假设,二进制小数0.000011000被舍弃的部分是末尾的1000,那么需要进1,因为未舍弃部分的最低位为1,舍入结果将是0.00010。
IEEE 754 二进制浮点数向零舍入,向正无穷舍入,向负无穷舍入规则
除了就近舍入规则,IEEE 754 还定义了向零舍入,向正无穷舍入,向负无穷舍入,他们可用于计算范围边界。
向零舍入会直接丢弃需要舍弃的部分,这样无论是正数还是负数都会更接近于0。
向正无穷舍入会直接丢弃负数需要舍弃的部分,如果正数被丢弃的部分含有1则进1,否则同样直接丢弃,这将保证无论是正数还是负数都会更接近正无穷。
向负无穷舍入会直接丢弃正数需要舍弃的部分,如果负数被丢弃的部分含有1则进1,否则同样直接丢弃,这将保证无论是正数还是负数都会更接近负无穷。