js 浮点数计算

2022-07-28  本文已影响0人  王果果

在做项目的时候,涉及到金额加减时,经常会出现计算精度的问题,常见例子如下:

     加法        

          0.1 + 0.2 = 0.30000000000000004

          0.7 + 0.1 = 0.7999999999999999

          0.2 + 0.4 = 0.6000000000000001

     减法

          1.5 - 1.2 = 0.30000000000000004

           0.3 - 0.2 = 0.09999999999999998

     乘法

           0.8 * 3 = 2.4000000000000004

           35.41 * 100 = 3540.9999999999995

      除法

           0.3 / 0.1 = 2.9999999999999996

           0.69 / 10 = 0.06899999999999999

在遇到浮点数运算后出现的精度问题时,起初想等加完之后使用toFixed(2)来解决的,toFixed()方法可把Number四舍五入为指定小数位数的数字

测试发现还是存在一定误差

为什么会产生

JavaScript中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用64位固定长度来表示,也就是标准的 double 双精度浮点数,

这样的存储结构优点是可以归一化处理整数和小数,节省存储空间。

64位比特又可分为三个部分:

符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数

指数位E:中间的 11 位存储指数(exponent),用来表示次方数

尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零

然后当javaScript运算时,会先把十进制的0.1和0.2会被转换成二进制的,但是由于浮点数用二进制表示时是无穷的:

   0.1 -> 0.0001 1001 1001 1001...(1100循环)

   0.2 -> 0.0011 0011 0011 0011...(0011循环)

IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持53位二进制位,所以两者相加之后得到二进制为:

   0.0100110011001100110011001100110011001100110011001100

因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了0.30000000000000004。所以在进行算术计算时会产生误差。

如何解决

大概思路就是把浮点数转化成整数(乘以10的n次幂)去加减,然后在降级处理(除以10的n次幂)

例如: 0.1 + 0.2 == 0.3 //false

       (0.1*10 + 0.2*10)/10 == 0.3 //true

本以为这样就能完美解决了,尴尬的事情出行了

 18.4 * 100 = 1839.9999999998 

所以最终决定把数值先toString之后通过split方法切割小数点,把小数点位数补成两位后,通过字符串拼接的方式拼接起来然后转成数值进行加减,最后在除以100即可。

代码如下:

//补全位数
function operation(num) {
//如果是整数的话默认补两位小数
if (num) {
num = num.slice(0,2);
return num.length === 1 ? num = num + '0' : num;
} else {
return '00'
}
}
//处理浮点数想加
var addNUm = function (num1,num2) {

  var array1 = num1.toString().split("."),//转化成字符串并已小数点切割 
  array2 = num2.toString().split(".");
  //如果是整数的话默认补两位小数
  array1[1] = operation(array1[1]);
  array2[1] = operation(array2[1]);
  //把整数部分和小数部分拼接起来然后在转成数字类型
  var num1 = Number(array1[0] + array1[1]),
  num2 = Number(array2[0] + array2[1]);
  //整数想加之后除以100
 return (num1 + num2) / 100;

}

addNUm(0.1,0.2);

上一篇下一篇

猜你喜欢

热点阅读