计算计处理浮点数(小数)的精准度问题(前端 & 后端都一样)
计算计处理浮点数的都会存在精准度的问题,这不是浏览器也不是js独有的,这是计算机的问题
image.png为什么会存在精准度问题呢?
-
数据类型值在计算机底层都是以二进制来进行存储的(undefined除外)【遵循IEEE-754规范、64位的二进制】
特点:
https://babbage.cs.qc.cuny.edu/IEEE-754.old/Decimal.html
第0位:符号位,0表示正数,1表示负数 S
第1位到第11位「11位指数」:储存指数部分 E
第12位到第63位「52位尾数」:储存小数部分(即有效数字)F
注:尾数部分在规约形式下第一位默认为1(省略不写) -
例如:在浏览器中,我们看到的数字都是十进制,但是存储到计算机底层就是二进制,所以存在把二进制转为10进制
@1、整数转二进制:除以2取余数(商为0结束),然后倒序排列,高位补零
image.png@2、小数:乘以2,取整数部分,剩余的计息乘以2...直到整数为1,没有小数部分了(特殊:很多浮点数转为二进制,结果都是无限循环的,但是计算机底层最多只能存储64位,超出的部分截掉;说明,我们存储到计算计底层的浮点数对应的二进制本身可能就是不准确的)
image.png
会出现无限循环
@3、浏览器中的十进制值是有长度限制的,一般是16-17位左右,所以说 0.1 + 0.2 =可能是0.300000000000000040000000000000000000000001002(
0.30000000000000004以后的数字是随便写的,为了验证下边的说法) -
浏览器再截取的时候,会把超出的部分截掉,裁掉之后如果后边都是0则省略掉,但凡有一位不是0,则不处理
-
0.1 + 0.2 = 0.30000000000000004,截掉超出长度部分后,最后一位是4,所以没办法省略,才有了我们现在看到的这个结果
-
0.1 + 0.3 为什么等于0.4呢?
很有可能0.1 + 0.3 = 0.40000000000000000000000000000000000000....
但是截完超出部分后结果是0.40000000000000000,0.4后边全是0,所以省略了,最后结果是0.4
浮点数以二进制存到计算机的时候,可能会出现无限循环的值,计算机最多只能截取64位(32位的电脑截取34位),本身就不是准确的,所以加了运算之后结果也不是准确的,而浏览器呢又会裁掉一部分,所以会出现浮点数运算的时候精准度丢失问题
但是项目中总会遇到浮点数运算,怎么保证精准度呢?
方法一:整体扩大系数
const coefficient = function coefficient(num){
num = num + ''
let [, char = ''] = num.split('.'),
len = char.length
// 长度是几,就是10的几次幂,就是我们要扩大的系数
// 比如长度是2,那就是10*10 = 100,参数要乘以100才能变成整数
// (如果整个计算完成后还是精度丢失,可以leng加1.加2...来保证准确性)
return Math.pow(10, len + 1)
}
const plus = function plus(n, m) {
// 先把参数通过隐式转换转成数字
n = +n
m = +m
if(isNaN(n) || isNaN(m)) return // 不是数字,直接返回
// 哪个参数的系数大,就取哪个
let coeff = Math.max(coefficient(n),coefficient(m))
return (n * coeff + m * coeff) / coeff
}
console.log(plus(0.1, 0.2))
方法二:第三方库:
Math.js
decimal.js
big.js....
方法三::toFixed()
最简单的方法,四舍五入,只保留小数点后两位
网友评论