js浮点数四则运算精度丢失以及toFixed()精度丢失解决方法

这篇具有很好参考价值的文章主要介绍了js浮点数四则运算精度丢失以及toFixed()精度丢失解决方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、js浮点数计算精度丢失的一些例子

1、四则运算精度丢失:

0.1+0.2 = 0.30000000000000004 
 
0.3 - 0.2 = 0.09999999999999998
 
10.22*100 = 1022.0000000000001
 
2.4/0.8 = 2.9999999999999996
 
32.2*100 = 3220.0000000000005
 
32.2*1000 = 32200.000000000004
 
(32.2*100 + 3.14*100) / 100 = 35.34 // 这里的精度怎么又不丢失了?
 
32.3*100 = 3229.9999999999995
 
32.3*1000 = 32299.999999999996
 
...

2、toFixed() 四舍五入精度丢失:

(1.335).toFixed(2); // '1.33'
(6.265).toFixed(2); // '6.26'

二、浮点数计算精度丢失的原因

js采用64位浮点数表示法(几乎所有现代编程语言所采用),这是一种二进制表示法。二进制浮点数表示法并不能精确表示类似 0.1 这样简单的数字。

这个问题不只在js中才会出现,在任何使用二进制浮点数的编程语言中都会出现。

JavaScript的未来版本或许会支持十进制数字类型以避免精度丢失的问题。

三、解决办法

1、使用 big.js(如果有大量连续的计算推荐使用)

  • 既解决了浮点数计算精度丢失问题,又解决了 toFixed() 四舍五入精度丢失问题。
  • big.jsbig.js, bignumber.js, decimal.js 三姐妹中功能最少的,但也是体积最小的,压缩版只有3k,对于处理js精度丢失已经足够用了。
  import Big from 'big.js'
 
  // 运算
  const plus = Big(0.1).plus(0.2); // 加
  const minus = Big(0.3).minus(0.1); // 减
  const mul = Big(10.22).times(100); // 乘
  const div = Big(2.4).div(0.8); // 除
 
  // toFixed
  const fixed = new Big(6.265).toFixed(2); // 6.27
 
  console.log(
    plus.toNumber(),
    minus.toNumber(),
    mul.toNumber(),
    div.toNumber()
  )
  // 0.3 0.2 1022 3

2、解决四则运算精度丢失问题

方法1:没有具体要求保留几位小数的,最简单的方法是直接用 toFixed()

从上面四则运算精度丢失的例子可以看到,四则运算的精度丢失主要会出现很多位 0 或很多位 9。

function precision(val) {
  return +val.toFixed(8);
}
 
precision(0.1 + 0.2)
方法2:有具体要求精确到第几位,用科学计数法对运算结果进行四舍五入

MDN 已经给出了具体代码(也是利用“科学计数法”扩大 10 的 n 次不会出现精度丢失的特性):

function round(number, precision) {
    return Math.round(+number + 'e' + precision) / Math.pow(10, precision);
}
 
round(1.005, 2);    //1.01
round(1.002, 2);    //1

或者:

/**
 * Decimal adjustment of a number.
 *
 * @param {String}  type  The type of adjustment.
 * @param {Number}  value The number.
 * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment base).
 * @returns {Number}      The adjusted value.
 */
function decimalAdjust(type, value, exp) {
    // If the exp is undefined or zero...
    if (typeof exp === 'undefined' || +exp === 0) {
        return Math[type](value);
    }
    value = +value;
    exp = +exp;
    // If the value is not a number or the exp is not an integer...
    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
        return NaN;
    }
    // Shift
    value = value.toString().split('e');
    value = Math[type](+(value[0] + 'e' + (value[1] ? +value[1] - exp : -exp)));
    // Shift back
    value = value.toString().split('e');
    value = +(value[0] + 'e' + (value[1] ? +value[1] + exp : exp));
    return value;
}
 
export default {
    round: (value, exp) => {
        return decimalAdjust('round', value, exp);
    },
    floor: (value, exp) => {
        return decimalAdjust('floor', value, exp);
    },
    ceil: (value, exp) => {
        return decimalAdjust('ceil', value, exp);
    }
};
实现原理:
比如 1022.0000000000001 要保留2位小数,先用 e2 把这个数扩大 100 倍,
再用 Math.round(), Math.floor(), Math.ceil() 取整,然后再用 e-2 缩小回来。
使用方法:
Decimal.round(val, precision)
 
console.log(Decimal.round(1.13265, -3))  //1.133
console.log(Decimal.round(3.17, -3))  //3.17
console.log(Decimal.round(0.1+0.2, -3))  //0.3
console.log(Decimal.round(3.17))  //3
console.log(Decimal.round(3.17, 0))  //3
console.log(Decimal.round(31216, 1))  //31220
console.log(Decimal.round(31213, 2))  //31200

precision 可选值:不传,0,负数,正数。

  • 不传、0: 精确到整数。
  • 正数: 1就是个位为0,十位是个位四舍五入的值。
  • 负数: 精确到小数点后几位

3、解决 toFixed() 精度丢失问题:重写 toFixed 方法(重点!!!!)

function toFixed(number, precision = 2) {
  number = Math.round(+number + 'e' + precision) / Math.pow(10, precision) + '';
  let s = number.split('.');
  if ((s[1] || '').length < precision) {
    s[1] = s[1] || '';
    s[1] += new Array(precision - s[1].length).fill('0').join('');
  }
  return s.join('.');
}
 
toFixed(6) // '6.00'

四、判断小数是否相等

function epsEqu(x,y) {  
  return Math.abs(x - y) < Math.pow(2, -52);
  // 因为 Number.EPSILON === Math.pow(2, -52),所以也可以这么写:
  // return Math.abs(x - y) < Number.EPSILON;
}
// 举例
0.1 + 0.2 === 0.3 // false
epsEqu(0.1 + 0.2, 0.3) // true
小数比较时,要给它一个误差范围,在误差范围内的都算相等。

五、其他由浮点数引起的问题

parseInt(0.0000008) // -> 8

六、项目内实际应用

js精度丢失解决办法,javascript,前端,开发语言

在列表上勾选5个涉案金额为0.055万元的案件,进行批量结案操作,在批量结案中,有一个减损值的计算,通过计算勾选案件涉案金额平均值-用户所填写的结案支付金额得出减损值,那么问题出现了,我在结案支付金额填写为0.055,正常计算的话结果应改为0,可是截图却如图所示:
js精度丢失解决办法,javascript,前端,开发语言
这里的业务代码为:

// 批量结案涉案金额取勾选数据的平均值
const amountInvolved = selectedRows.reduce((c, R) => c + (R.amountInvolved - 0), 0) / selectedRows.length

//计算减损值
OnchangeMoney(value) {
      this.mdl.impairmentValue = this.req.amountInvolved - value
},

打断点发现是计算平均值amountInvolved时出现了浮点数,那么封装一个方法:文章来源地址https://www.toymoban.com/news/detail-753643.html

function strip(num, precision = 12) { 
  return +parseFloat(num.toPrecision(precision)); 
}  
为什么选择 12 做为默认精度?
这是一个经验的选择,一般选12就能解决掉大部分0001和0009问题,
而且大部分情况下也够用了,如果你需要更精确可以调高。
  • 处理平均数计算:
// 批量结案涉案金额取勾选数据的平均值
const amountInvolved = this.strip(
      selectedRows.reduce((c, R) => c + (R.amountInvolved - 0), 0) / selectedRows.length
 )
  • 运行之后发现还是有浮点数,打断点是计算差值是也出现了浮点数,解决:
//计算金额
OnchangeMoney(value) {
   this.mdl.impairmentValue = this.strip(this.req.amountInvolved - value)
},

参考(JS 计算最小值,最大值,平均值,标准差,中位数):

      // @numbers 包含所有数字的一维数组
      // @digit 保留数值精度小数位数,默认两位小数
      function getBebeQ(numbers, digit = 2) {
        // 修复js浮点数精度误差问题
        const formulaCalc = function formulaCalc(formula, digit) {
          let pow = Math.pow(10, digit);
          return parseInt(formula * pow, 10) / pow;
        };
        let len = numbers.length;
        let sum = (a, b) => formulaCalc(a + b, digit);
        let max = Math.max.apply(null, numbers);
        let min = Math.min.apply(null, numbers);
        // 平均值
        let avg = numbers.reduce(sum) / len;
        // 计算中位数
        // 将数值从大到小顺序排列好,赋值给新数组用于计算中位数
        let sequence = [].concat(numbers).sort((a,b) => b-a);
        let mid = len & 1 === 0 ?
          (sequence[len/2] + sequence[len/2+1]) / 2 :
          sequence[(len+1)/2];
        // 计算标准差
        // 所有数减去其平均值的平方和,再除以数组个数(或个数减一,即变异数)再把所得值开根号
        let stdDev = Math.sqrt(numbers.map(n=> (n-avg) * (n-avg)).reduce(sum) / len);
        return {
          max,
          min,
          avg: avg.toFixed(digit),
          mid: parseFloat(mid).toFixed(digit),
          stdDev : stdDev.toFixed(digit)
        }
      }

到了这里,关于js浮点数四则运算精度丢失以及toFixed()精度丢失解决方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • js精度丢失的问题

    1.js精度丢失的常见问题,从常见的浮点型进行计算,到位数很长的munber类型进行计算都会造成精度丢失的问题, 首先我们看一个问题: 那么js为什么会出现精度丢失的问题: 计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1

    2024年02月14日
    浏览(50)
  • MyBatis-Plus主键策略(雪花算法16位长度的整型id,解决默认雪花算法生成19位长度id导致JS精度丢失问题)

    js表达的最大整数2的53次方减1,精度丢失后面几位全是0! 如果内置支持不满足你的需求,可实现 IKeyGenerator 接口来进行扩展. 举个栗子 #方式一:使用配置类 #方式二:通过 MybatisPlusPropertiesCustomizer 自定义 #方式一: XML 配置 #方式二:注解配置 官方示例 官方id generator示例 htt

    2023年04月08日
    浏览(43)
  • java数据类型的转换以及精度丢失

    float存储需求是4字节(32位), 其中1位最高位是符号位,中间8位表示阶位,后32位表示值 double存储需求是8字节(64为),其中1位最高位是符号位,中间11位表示阶位,后52位表示值 精度丢失就是我们的位数不够表示我们整个数值了 问题原因: 首先计算机进行的是二进制运算,

    2023年04月13日
    浏览(44)
  • Unity - 记录解决 部分手机设备上 浮点精度 不足 导致 UV 采样断层严重的 BUG

    Unity : 2020.3.37f1 Pipeline : BRP 备忘,便于日后索引 正常 手机显卡芯片的浮点解析进度上的效果(其实不用手机上,PC 上将 uv * scale 一个巨大的值也会出现的) 异常 手机显卡芯片的浮点解析进度上的效果(其实不用手机上,PC 上将 uv * scale 一个巨大的值也会出现的) 诊断发现是

    2024年02月11日
    浏览(90)
  • 完美解决!处理精度丢失问题点

    目录 1. 解决后端响应数据给前端出现精度丢失问题 2. Freemark BigDecimal数据显示精度丢失问题 3. 前端调用方法传值精度丢失问题 解决方式一: 在项目中都是将注解标注在对应字段上,在Json序列化的时候把Long自动转为String。  解决方式二: 全局配置 每个实体类的id字段都需要

    2024年02月15日
    浏览(70)
  • 前端计算数字精度丢失问题解决方法记录

    在日常一些需求中,总会遇到一些需要前端进行手动计算的场景,那么这里需要优先考虑的则是数字精度问题!具体请看下面截图 如图所示,在JavaScript进行浮点型数据计算当中,会出现计算结果“不正确”的现象。 我们知道浮点型数据类型主要有:单精度float、双精度doub

    2024年02月05日
    浏览(74)
  • 如何解决前端传递数据给后端时精度丢失问题

    有时候我们在进行修改操作时,发现修改既不报错也不生效。我们进行排查后发现服务器端将数据返回给前端时没有出错,但是前端js将数据进行处理时却出错了,因为id是Long类型的,而js在处理后端返回给前端的Long类型数据时只能处理前16位,后3位进行了四舍五入操作,例

    2024年02月09日
    浏览(63)
  • 如何完美解决前端数字计算精度丢失与数字格式化问题?

    大家好,我是木瓜太香,做前端开发经常会遇到数字计算精度丢失的问题,和数字格式化的麻烦问题,好不容易找到了可以解决这些问题的库结果用起来不够方便,例如 bignumber.js decimal.js 等编写体验不好,这篇文章来帮助你完美解决这些问题 接下来我们根据以下两个问题展

    2024年02月16日
    浏览(47)
  • SpringBoot返回前端Long类型字段丢失精度问题及解决方案

    Java服务端返回Long整型数据给前端,JS会自动转换为Number类型。而Long类型能表示的最大值为(),当数值超过JS中Number类型的最大值()时,就会丢失精度。 首先,引入依赖。 新建一个自定义大数据序列化类,如下: 新建Jackson配置类 本文针对Java服务端返回Long整型数据给前端时

    2024年03月22日
    浏览(105)
  • 2.6 浮点运算方法和浮点运算器

      以下是一些具体的学习目标: 理解浮点数的基本概念和表示方法,包括符号位、指数和尾数。 学习浮点数的运算规则和舍入规则,包括加、减、乘、除、开方等。 了解浮点数的常见问题和误差,例如舍入误差、溢出、下溢等,并学会如何处理这些问题。 理解浮点运算器的

    2024年02月01日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包