js小数点计算丢失精度

  有时需求中会有前端校验输入数字金额的时候,判断,几个输入框的金额合计是否大于小于或等于某个整数,在输入的值可以为小数的时候,很容易就出现js小数点计算丢失精度问题。比如下图

  js高级程序设计(我这版是第3版)在3.4.5Number类型这节中就谈到了这个现象,原话是:

    关于浮点数值计算会产生摄入误差的问题,有一点需要明确:这是使用基于IEEE754数值的浮点计算的通病,ESMAScript并非独此一家,其他使用相同数值格式的语言也存在这个问题。

  所以即使浮点数值的最高精度是17位小数,但在进行算术计算时其精度远远不如整数。

  1.使用toFixed(x)方法,x为必需,规定小数的位数,是 0 ~ 20 之间的值,包括 0 和 20,如果省略了该参数,将用 0 代替。

  比如:

   但是这种方法的局限性是不能使用toFixed(x)去进行舍入操作。因为IEEE754标准规定的浮点数取整算法是银行家舍入法,即四舍六入五留双法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

  极其不推荐对小数有舍入操作的时候使用toFixed(x)。

  小数点计算的时候精度只能用于所有操作数中最多小数位的精度计算,即toFixed(x)中的x必须大于等于最多小数位操作数的小数位数量,否则就会丢失精度。

  2.使用第三方库

  比如Math.js,decimal.js,使用方法大同小异,详情见:

  https://www.npmjs.com/package/mathjs

  https://www.npmjs.com/package/decimal.js/v/3.0.0

  以vue2.x为例:

  先npm install mathjs / decimal.js

  然后在main.js中引入,以ES模块规范为例:

  math.js是这样使用的:(详见math.js文档:https://mathjs.org/docs/index.html)

import * as math from "mathjs";

const config = {
  epsilon: 1e-12,
  matrix: 'Matrix',
  number: 'number', //运算时需要精度准确时此处需配置为BigNumber
  precision: 64, //仅在number类型为BigNumbers生效
  predictable: false,
  randomSeed: null //选项设置为种子伪随机数生成,使其成为确定性的。
}
const math = create(all, config);

let number = math.add(math.bignumber(0.1), math.bignumber(0.2)); //操作数中至少要有一个调用bignumber()

  decimal.js是这样使用的:(详见decimal.js文档:http://mikemcl.github.io/decimal.js/)

import { Decimal } from 'decimal.js';

let number = new Decimal(0.11).add(new Decimal(0.29));

其他

  本来到前面就到尾声了,但是这里还是需要啰嗦一下,我还看过一些博客还提到一个方法或是自己封装的方法,其核心是先将小数*10的n次方放大为整数,只要最终结果在Number类型边界之内的整数运算是不会有精度误差的,然后再将整数的运算结果除于10的n次方就得到最终的结果。但是,这个方法没写好也是存在问题的,就比如前面举的例子:0.07*100,结果就不是7。因为只有少数几个小数可以被浮点数精确表示,比如0.25的N次方,或是0.75的n次方,其他的小数基本都是近似数。其实我也很纳闷0.06,0.08也都是近似数,但是他们相乘100结果分别就是6,8。有些博主通过这个方法封装的方法都没考虑到这块,所以在相乘放大过程中就已经出错了,但是如果在放大缩小过程中能控制住精度,也仍不失为一个解决问题的好方法。

 

posted @ 2022-03-07 17:21  他好像一条狗啊  阅读(1442)  评论(0编辑  收藏  举报