您的位置:首页 > Web前端 > JavaScript

js小数点计算丢失精度

2022-03-07 17:21 369 查看 https://www.cnblogs.com/jdwu-d

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

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

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

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

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

  比如:

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

  早期的时候似乎在不同浏览器会有不同的表现,但是现在基本上大部分浏览器都是toFixed(x)遇5不会进1,而是直接舍去,可能也和chrome内核一家独大有关系。在Chrome浏览器中的控制台表现如下:

  精度只能用于所有操作数中最多小数位的精度计算,即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。有些博主通过这个方法封装的方法都没考虑到这块,所以在相乘放大过程中就已经出错了,但是如果在放大缩小过程中能控制住精度,也仍不失为一个解决问题的好方法。

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: