JavaScript 的数学还不如小学生,罪魁祸首竟然是它

124 阅读2分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战

JavaScript 的数学还真是不太好,小学生看到的话,都得说一声:就 这 ?

不服气的,就往下看 ⬇️

let a = 1
let b = 2
let c = 3
let d = a + b
let e = b + c
console.log('a:', a, 'b:', b, 'c:', c, 'd:', d, 'e:', e)
// 打印结果 a: 1 b: 2 c: 3 d: 3 e: 5

这打印结果也没毛病呀。。。全对 ✅

那我们再来看看小数的计算。。。

let a = 0.1
let b = 0.2
let c = 0.3
let d = a + b
let e = b + c
console.log('a:', a, 'b:', b, 'c:', c, 'd:', d, 'e:', e)
// 打印结果 a: 0.1 b: 0.2 c: 0.3 d: 0.30000000000000004 e: 0.5

咦 ~ ~ ~

0.1 + 0.2 竟然不等于 0.3

0.1+0.2 = 0.3,这连小学生都会算的题,咋会出错呢。。。这又是咋回事呢?到底是谁在捣鬼???

探探号要发车咯,我们一起去找找原因吧

探探第一站 —— IEEE 754

  • 二进制浮点数算术标准
  • 浮点数的四种表示方式 【 单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现) 】
  • 浮点数 = 符号位(sign)✖️ 指数偏移值(exponent) ✖️ 分数值(fraction)
  • 单精度浮点数(共32位)= 1个符号位 + 8个指数位 + 23个小数位
  • 双精度浮点数(共64位)= 1个符号位 + 11个指数位 + 52个小数位
  • 浮点数通过利用指数达到了浮动小数点的效果,可以表达更大范围的实数,但同时也会产生误差

探探第二站 —— 误差取舍

  • 默认向偶数舍入 【 取最接近且可表示的值 —— 只需考虑最低有效数字是奇数还是偶数 】
    • 1.4 -> 1; 1.5 -> 2; 1.6 -> 2; 2.5 -> 2; -1.5 -> -2
  • 向 0 舍入
    • 1.4 -> 1; 1.5 -> 1; 1.6 -> 1; 2.5 -> 2; -1.5 -> -1
  • 向上舍入 【 +∞ 方向 】
    • 1.4 -> 2; 1.5 -> 2; 1.6 -> 2; 2.5 -> 3; -1.5 -> -1
  • 向下舍入 【 -∞ 方向 】
    • 1.4 -> 1; 1.5 -> 1; 1.6 -> 1; 2.5 -> 2; -1.5 -> -2

探探第三站

  • 鉴于浮点数不可避免滴会有误差的情况出现,不推荐在计算中使用浮点数
  • 前提已知最终计算结果的小数点位数,可以先把小数转换为整数,再进行加减,得到的结果,再除以其转为整数时扩大的倍数
     let a = 0.1
     let b = 0.2
     let c = (a * 10 + b * 10 ) / 10
     console.log(c) // 输出结果 0.3
    

终点 🏁

  • 万万没想到 JavaScript 的数学不好的背后,竟然是二进制浮点数算术标准在作怪