【JS计算精度】0.1+0.2=0.30000000000000004

362 阅读2分钟

引言

在日常的业务中我经常会遇到需要对数字进行计算处理,也就会出现一个JS中很经典的问题,0.1+0.2 ≠ 0.3。本文将对这种现象原因进行分析,以及解决方案。

为什么0.1 + 0.2 ≠ 0.3

对于一些强类型的编程语言会对数字的类型分为整数、浮点数等多种类型,但JS 只有Number类型,它是按照国际 IEEE 754标准,将数字都存储为双精度浮点数。

IEEE二进制浮点数算术标准IEEE 754是20世纪80年代以来最广泛使用的浮点数运算标准。

这种格式以 64 位存储数字,其中数字(分数)存储在位 0 到 51 中,指数存储在位 52 到 62 中,符号存储在位 63 中。

在计算机使用二进制数来进行计算,首先我们将它们转化为二进制数,根据IEEE 754的标准,最多我们只能保存53位,但由于计算百机内部以二进制保存,所以十进制的有限位的小数,在计算机内部会是一个无限位的小数

例如: 十进制的0.9虽然只有一位小数,转成2进制道是无限循环小数0.1110011001100110011...

所以多出来舍去,0舍1入。

所以在计算机中除了可以表示为2的幂次以及整数数乘的浮点数可以准确表示外,其余的数的值都是近似值。

所以我们就得到0.1 和 0.2的近似值

0.1: 0.0001100110011001100110011001100110011001100110011010
0.2: 0.0011001100110011001100110011001100110011001100110011
0.1 + 0.2: 0.0100110011001100110011001100110011001100110011001101

计算完成后,又将二进制数转化为浮点数

image.png

这就是 0.1 + 0.2 = 0.30000000000000004 的原因。

怎么让0.1 + 0.2 = 0.3

手写

解决计算精度主要思路就是通过将浮点数计算都转化为整数计算,最后再除以扩大的倍数,来确保计算的正确性。使用这种方式要注意JS中的最大的安全数

MAX_SAFE_INTEGER: 9007199254740991

MIN_SAFE_INTEGER: -9007199254740991

// 精确加法
function add(num1, num2) {
  const num1Digits = (num1.toString().split(".")[1] || "").length;
  const num2Digits = (num2.toString().split(".")[1] || "").length;
  const magnification = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (Math.floor(num1 * magnification) + Math.floor(num2 * magnification)) / magnification;
}

add(1.1, 2.2) // 3.3

// 精确乘法
function multiply(num1, num2) {
  const num1Digits = (num1.toString().split(".")[1] || "").length;
  const num2Digits = (num2.toString().split(".")[1] || "").length;
  const magnification = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (
    (Math.floor(num1 * magnification) * Math.floor(num2 * magnification)) /
    (magnification * magnification)
  );
}

add(1.1, 2.2) // 2.42

开源推荐

在日常项目中,我经常使用这个库来实现JS的相关计算需求number-precision,可以满足大部分计算需求。

import NP from 'number-precision'

NP.plus(0.1, 0.2);             // = 0.3

也可以使用math.js来满足各种复杂的数学计算场景。