• 都知道0.1+0.2 = 0.30000000000000004,那要怎么让它等于0.3


    前言

    小学数学老师教过我们,0.1 + 0.2 = 0.3,但是为什么在我们在浏览器的控制台中输出却是0.30000000000000004?

    除了加法有这个奇怪的现象,带小数点的减法和乘除计算也会得出意料之外的结果

    console.log(0.3 - 0.1) // 0.19999999999999998
    console.log(0.1 * 0.2) // 0.020000000000000004
    console.log(0.3 / 0.1) // 2.9999999999999996
    

    原因

    我们都知道计算机时是通过二进制来进行计算的,即 0 和 1

    就拿 0.1 + 0.2 来说,0.1表示为0.0001100110011001...,而0.2表示为0.0011001100110011...

    而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100...

    转成10进制就近似表示为 0.30000000000000004

    结论

    简单来说就是,浮点数转成二进制时丢失了精度,因此在二进制计算完再转回十进制时可能会和理论结果不同

    对于浮点数的四则运算,许多编程语言都会有理论值和实际值不同的问题。例如Java中也会出现类似的问题,但是Java中可以使用java.math.BigDecimal类来避免这种情况

    可是JS是弱类型的语言,作者Brendan Eich自述10天内开发出JS语言,一开始设计的时候就没有对浮点数计算有个处理的好方法

    那么在日常开发的前端项目中我们可以怎么解决嘞?

    解决方案

    简单实现

    使用toFixed()<不推荐>

    可以控制小数点后几位,如果为空的话会用0补充,返回一个字符串

    > 0.123.toFixed(2) // '0.12'
    

    缺点:

    • 在不同浏览器中得出的值可能不相同,且部分数字得不到预计的结果,并不是执行严格的四舍五入
    // 在chrome控制台中
    > 1.014.toFixed(2) // '1.01'
    > 1.215.toFixed(2) // '1.22'
    > 1.105.toFixed(2) // '1.10'
    > 1.115.toFixed(2) // '1.11'
    

    乘以一个10的幂次方

    把需要计算的数字乘以10的n次方,让数值都变为整数,计算完后再除以10的n次方,这样就不会出现浮点数精度丢失问题

    > (0.1 * 10 + 0.2 *10) / 10  // 0.3
    

    我们可以将它封装成一个函数

    mathFloat = function (float, digit) {
      const math = Math.pow(10, digit);
      return parseInt(float * math, 10) / math;
    }
    mathFloat(0.1 + 0.2, 3)  // 0.3
    

    缺点:

    • JS中的存储都是通过8字节的double浮点类型表示的,因此它并不能准确记录所有数字,它存在一个数值范围

      Number.MAX_SAFE_INTEGER为 9007199254740991,而Number.MIN_SAFE_INTEGER为 -9007199254740991,超出这个范围的话JS是无法表示的
      虽然范围有限制,但是数值一般都够用

    较为完整的实现

    加法

    function mathPlus(arg1, arg2) {
      let r1, r2, m;
      try {
        r1 = arg1.toString().split(".")[1].length; // 获取小数点后字符长度
      } catch (error) {
        r1 = 0; // 为整数状态,r1赋0
      }
      try {
        r2 = arg2.toString().split(".")[1].length;
      } catch (error) {
        r2 = 0;
      }
      m = Math.pow(10, Math.max(r1, r2)); // 确保所有参数都为整数
      return (arg1 * m + arg2 * m) / m;
    }
    > mathPlus(0.1, 0.2); // 0.3
    > mathPlus(1, 2); // 3
    

    减法

    function mathSubtract(arg1, arg2) {
      let r1, r2, m;
      try {
        r1 = arg1.toString().split(".")[1].length;
      } catch (error) {
        r1 = 0;
      }
      try {
        r2 = arg2.toString().split(".")[1].length;
      } catch (error) {
        r2 = 0;
      }
      m = Math.pow(10, Math.max(r1, r2));
      return ((arg1 * m - arg2 * m) / m);
    }
    > mathSubtract(0.3, 0.1); // 0.2
    > mathSubtract(3, 1); // 2
    

    乘法

    function mathMultiply(arg1, arg2) {
      let m = 0;
      let s1 = arg1.toString();
      let s2 = arg2.toString();
      try {
        m += s1.split('.')[1].length; // 小数相乘,小数点后个数相加
      } catch (e) {}
      try {
        m += s2.split('.')[1].length;
      } catch (e) {}
      return (
        (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) /
        Math.pow(10, m)
      );
    }
    > mathMultiply(0.1, 0.2); // 0.02
    > mathMultiply(1, 2); // 2
    

    除法

    function mathDivide(arg1, arg2) {
      let m1 = 0;
      let m2 = 0;
      let n1 = 0;
      let n2 = 0;
      try {
        m1 = arg1.toString().split('.')[1].length;
      } catch (e) {}
      try {
        m2 = arg2.toString().split('.')[1].length;
      } catch (e) {}
      n1 = Number(arg1.toString().replace('.', ''));
      n2 = Number(arg2.toString().replace('.', ''));
       /**
       * 将除法转换成乘法
       * 乘以它们的小数点后个数差
       */
      return mathMultiply(n1 / n2, Math.pow(10, m2 - m1));
    }
    // > 0.2 / 0.03 => 6.666666666666667
    > mathDivide(0.2, 0.03); // 6.666666666666665
    > mathDivide(0.3, 0.1); // 3
    > mathDivide(3, 1); // 3
    

    引入第三方库

    站在前人的肩膀上,可以前进的更快。下面这些成熟的库封装了很多实用的函数,虽然部分函数可能永远不会用到

    Math.js

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

    decimal.js

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

    big.js

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

    number-precision

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

  • 相关阅读:
    Qt中定时器的3种实现方法
    Java摆烂基础学习三~程序流程控制
    java计算机毕业设计网上书城网站源代码+数据库+系统+lw文档
    自己动手从零写桌面操作系统GrapeOS系列教程——4.GrapeOS开发环境介绍
    微信小程序&会议OA-登录获取手机号流程&登录-小程序&导入微信小程序SDK(从微信小程序和会议OA登录获取手机号到登录小程序导入微信小程序SDK)
    初识TOML-配置文件格式
    程序员追星 - Gerald Jay Sussman
    一篇文章入门单链表+刷题实践【java实现+详细注释】
    Three.JS程序化建模入门
    SAP Java Connector 错误 - JCO_ERROR_COMMUNICATION
  • 原文地址:https://www.cnblogs.com/rainy-night/p/16091646.html