0.1 + 0.2 !== 0.3 ?

157 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

0.1 + 0.2 !== 0.3 ?

引言

  • 我们平时做的数学题:加法运算,从小的口诀就是逢十进一,也就是十进制。你可能还听说过“半斤八两”,意思是两个人水平差不多。放在数学的角度,半斤可以是十进制去表示的,表示一半,八两如果也想表示一半,那他自然就是十六进制。所以站在进制的角度去看这个问题,半斤(十进制) 就是 八两(十六进制)。在计算机里面,是用另一种进制表现的,那就是二进制。他的加法口诀自然就是逢二进一

js中的数字类型

在 js中使用Number类型表示数字(整数和浮点数)。遵循IEEE-754标准,通过64位二进制来表示一个数字。

64位的详解

第0位:符号位,0表示正数,1表示负数

第1 - 11位:【11位指数】:存储指数部分

第12 - 63位:【52位尾数】:存储小数部分

注: 尾数部分在规定形式下,第一位默认是1(省略不写)

最大安全数[16位]

  • 获取最大安全数
Number.MAX_SAFE_INTEGER -》 9007199254740991

Number.MAX_SAFE_INTEGER === Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1

52(位尾数) + 1(省略不写的尾数) - 1(符号位)

  • 获取最小安全数(最大安全数的负数)
Number.MIN_SAFE_INTEGER -》 -9007199254740991

js中关于小数(浮点数)计算,精度丢失的问题

  • 计算机在底层存储的都是0,1的二进制(浮点数转化为二进制,可能会出现无限循环的情况)

0.1 + 0.2 !== 0.3

我们来看看这两个十进制转成二进制是多少


(0.1).toString(2) // '0.0001100110011001100110011001100110011001100110011001101'
(0.2).toString(2) // '0.001100110011001100110011001100110011001100110011001101'
0.1+ 0.2 // 0.30000000000000004

十进制转二进制

  • 简单的进制转化,我们可能口算就可以,但是复杂一点,就需要公式来进行计算了

  • 比如把十进制6.36 转换为二进制

image.png

  • 整数部分:

用整数部分,每次 ÷ 2 ,得到的余数位的倒叙,就是整数转化为二进制的整数部分

  • 小数部分

每次 * 2, 得到的结果如果小于1,则补零。大于1 则将 1取出,继续相乘

  • 结果: 最终结果可能出乎你的意外。出现了循环。浮点数不能精确地用二进制表示所有小数。这可能会导致意外的结果

怎么解决精度问题?

toFixed(digits)

toFixed()  方法使用定点表示法来格式化一个数值。会四舍五入

  • digits 小数点后数字的个数;介于 0 到 20(包括)之间,实现环境可能支持更大范围。如果忽略该参数,则默认为 0。

image.png 这样尽管可以解决一些问题,但还会有一些其他问题

image.png 保留两位小数,本来结果是 0.11 ,但是却返回了 0.10

扩大系数法

  • 既然浮点数计算,会出现精度问题,那我们就把十进制浮点数,通过扩大倍数,将其转化为整数。二进制计算完毕,对十进制再除以系数还原即可。

代码

加法: 核心是找出小数位数最多的位数

const coefficient = (num) => {
  num += '';
  let [, char = ''] = num.split('.');
  let len = char.length;
  return Math.pow(10, len); // 返回要扩大的系数
  // return 10 ** len
};
const plus = (num1, num2) => {
  num1 = +num1;
  num2 = +num2;
  if (isNaN(num1) || isNaN(num2)) return NaN;
  let max = Math.max(coefficient(num1), coefficient(num2));
  return (max * num1 + max * num2) / max;
};
console.log(plus(0.1, 0.2)); // 0.3

引入三方库

摘录来自(juejin.cn/post/708180…)

Math.js

介绍:功能强大,内置大量函数,体积较大
Github地址:github.com/josdejong/m…
star: 12.2k+

decimal.js

介绍:支持三角函数等,并支持非整数幂
Github地址:github.com/MikeMcl/dec…
star: 4.8k+

big.js

介绍:体积6k,提供了CDN
Github地址:github.com/MikeMcl/big…
star: 3.9k+

number-precision

介绍:体积很小,只有1k左右 Github地址:github.com/nefe/number…
star: 3.4k+

下一篇,讲解一下 number-precision 库的具体实现