js 小数运算出现误差的原因

问题

在javascript使用小数点 +-*/ 运算会出现误差,比如:

0.1 + 0.2 == 0.30000000000000004
0.2 + 0.4 == 0.6000000000000001
19.9 * 100 == 1989.9999999999998

其实,不仅仅只有javascript,还有java、python等都会有类似问题,因为浮点数IEEE754是被普遍使用的标准

 


 

浮点数

浮点数是相对于定点数来说的。

计算机中小数的表示法,有定点和浮点两种。

定点,即小数点固定,比如:302876512411.25 小数点固定在数字的个位数右边
浮点,即小数点不固定(浮动),3.028e+11 小数点不固定在个位数和小数之间,而是根据指数值进行前后浮动

可以这么理解,科学记数法就是浮点数的表示方式

那么,我们为什么要使用浮点数呢?

我们可以先考虑下,为什么要使用科学记数法?

302876512411.25
3.028e+11

科学记数法的核心就是:通过移动小数点,只在小数点前保留一位数字,其他都算作小数,并使用指数记录小数点移动的位数

好处是,通过指数就可以很直观的看出数值的大小(而不用个十百千万的数)

其实它还有另一个好处,如果省略小数点后几位的话,它会显得很廋,不像原值那么臃肿(缺点是精度丢失)

 


 

计算机中的浮点数

因为计算机中,数值的存储是有大小限制的,比如

单精度浮点数 float -- 4Byte -- 32bit
双精度浮点数 double -- 8Byte -- 64bit

问题: 如何在有限的存储空间内容,尽可能的表示更多的数值?

使用定点数(原值),虽然保留了精度,但是能够表示的数值范围有限
使用浮点数(科学记数法),能够表示的数值范围变广了,但是精度也丢失了

也就是说,相同位数下

范围和精度是不可兼得

两害相权取其轻,微小的精度 没有 数值表示范围 显得更重要

所以计算机中表示小数的方式就是使用了浮点数,也就有了 IEEE754标准

 


 

二进制的定点和浮点

定点数: 以32位存储为例 (124 + 1*2e-2)

img
图片来源: https://www.zhihu.com/question/19848808

浮点数:(1.01)2 = 1.25 即 1.25 * 2e-3

img
图片来源: https://www.zhihu.com/question/19848808

阶码=阶码真值+127。 (127是单精度浮点的偏移量,即 0111 1111)

 


 

IEEE 754

浮点数的存储格式,一般按照标准IEEE 754

IEEE 754 规定,浮点数的表示方法为:

img

最高的 1 位是符号位 s,(表示正负)

接着的 8 位是阶码真值E,(补码,计算真值需要加上偏移量)

剩下的 23 位为尾数 M。(原码)

float: 1 + 8 + 23 = 32

double: 1 + 11 + 52 = 64

例如:0.125 DEC = 1/8 = 0.001 BIN = 1 x 2^-3

= 0 + 0111 1100 + 0000 0000 0000 0000 0000 000 = 00111110000000000000000000000000

IEEE754换算工具: http://www.binaryconvert.com/convert_double.html

 


 

浮点数加减乘除运算出现误差的原因

0.125 = 1/8

0.0625 = 1/16

0.03125 = 1/32

0.015625 = 1/64

0.0078125 = 1/128

0.00390625 = 1/256

0.1 = 0/2 + 0/4 + 0/8 + 1/16 + 1/32 + 0/64 + 0/128 + 1/256 + 1/512 + ...

0.1 = 0.0001 1001 1001 ...

 

0.2 = 0/2 + 0/4 + 1/8 + 1/16 + 0/32 + 0/64 + 1/128 + 1/256 + 0/512 + ...

0.1 = 0.0011 0011 0011 ...

原因: 一个能准确表示的十进制小数,而二进制却是循环小数

解决方法

避免使用会清除小数的换算方式

Math.floor
Math.ceil

 

对于整数,一般不会出错
对于小数,出错的概率较高,可以先变为整数,再缩为小数 (0.1*10 + 0.2*10) / 10 == 0.3
小数的加减乘除运算可以封装方法: https://blog.51cto.com/xzllff/831241

posted @ 2021-07-20 15:35  南歌子  阅读(1955)  评论(0编辑  收藏  举报