js浮点数四则运算精度丢失和toFixed()精度丢失解决方法_js除法精度丢失-程序员宅基地

技术标签: JavaScript  前端  javascript  开发语言  

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

四则运算精度丢失:

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

...

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

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

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

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

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

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

三、解决办法

使用 big.js

如果有大量连续的计算推荐使用

既解决了浮点数计算精度丢失问题,又解决了 toFixed() 四舍五入精度丢失问题。

big.js big.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

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

方法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,十位是个位四舍五入的值。
  • 负数: 精确到小数点后几位

解决 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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/huangpb123/article/details/80209577

智能推荐

ECharts画分类甘特图_echarts 甘特图分层级-程序员宅基地

文章浏览阅读2.1k次。项目最近做任务排产,需要绘画甘特图,并且每两份一组,做了一个JQuery快速做了POC,实现了大部分功能,后来持续开发,做到了划定当前时间,任务多了后固定左侧列和顶部坐标轴行等等。分享博客的话就简单的分享一下做的POC吧。实现的初步效果如下分析:左侧的轴样式美观程度比较高,且需要分组,分类,我初次尝试echarts分组,样式自定义程度不好,距离还得xy算偏移量左侧使用css 右侧使用echarts,则会出现图表和css自定义的样式行对不上等等,分类随时还需要空一行每个不同的生产内容颜色区块._echarts 甘特图分层级

中小企业业财融合策略(2):财务赋能,如何支持业务经营?_业务经营支持与赋能-程序员宅基地

文章浏览阅读231次。前文我们谈到了,企业业务流程的规范性,决定了企业经营相关信息、数据获取的准确性和及时性,很多时候,财务人员对流程的规范性、完整性感知更深刻,因此企业的业务流程建设,应该要让财务参与进来,一方面:财务可以从信息流、资金流、物流协同,及企业成本和收入的角度对流程进行审视。财务由被动转为主动,并不能仅仅是财务的主观愿望,而应该是公司管理的主动安排,例如:规定每个期间财务部要对公司的运营部门做《评价报告》,该报告直接交给总经理,报告内容,侧重在过程指标的展示说明,这也是体现财务分析能力的关键!_业务经营支持与赋能

安装Ubuntu 出现ubi partman crashed,ubi-partman failed with exit code 10-程序员宅基地

文章浏览阅读6.5k次。出现这个问题好像是因为硬盘中有遗留的raid信息导致。在安装选项中添加 nodmraid参数,再启动进行安装就好了如果不会添加参数可以参考这篇文章:安装ubuntu时黑屏三种解决办法就和添加 nomodeset参数一样转载于:https://www.cnblogs.com/xiyu714/p/10125984.html..._ubi-partman crashed

Linux下找不到IFCONFIG命令_linux系统ifconfig找不到?-程序员宅基地

文章浏览阅读2.3w次。# ifconfig  提示命令不存在  使用 # /sbin/ifconfig 即可  原因: 系统默认的环境变量设置不对  在 普通用户 和root用户下分别执行echo $PATH,PATH里少了四个地址:/sbin:/usr/sbin:/usr/local/sbin:/usr/kerberos/sbin  而,ifconfig恰恰就在/sbin里面。_linux系统ifconfig找不到?

在APK中获取鸿蒙应用Ability信息_getbundleinfo-程序员宅基地

文章浏览阅读4.2k次。Android开发工具箱大概在版本2.2.0(2021-06-10)就已经支持查看鸿蒙系统信息以及鸿蒙应用信息了。这里我讲一下Android开发工具箱是如何在Android应用中(APK)获取鸿蒙应用Ability信息(类似于Android应用的四大组件信息)Android开发工具箱可在应用宝、酷安下载,微信公众号:Android开发工具箱https://www.coolapk.com/apk/com.su.assistant.proAndroid中,我们获取应用列表之后,再获取应用的P_getbundleinfo

QT QModbus Rtu Serial Master 分包_qt头文件没有qmodbus-程序员宅基地

文章浏览阅读225次。使用单独线程控制modbus收发数据,信号槽交互UI和线程间的数据,信号槽自定义结构体发送,废话不多说,上代码。//如下自定义结构体,方便信号槽发送。欢迎加入QQ群538536725灌水。//最后是qt pro配置文件。//如下主UI头文件。_qt头文件没有qmodbus

随便推点

uniapp动态修改页面标题_uniapp更改页面名称-程序员宅基地

文章浏览阅读3.2k次。// 动态修改title uni.setNavigationBarTitle({   title:title })_uniapp更改页面名称

基于MALAB的B样条曲线逼近算法(抗噪声、误差可控、支持开/闭合)-程序员宅基地

文章浏览阅读426次,点赞12次,收藏5次。给定一组有序的数据点(存在很多噪声),求解一条B样条曲线逼近于该组数据点,每个数据点到曲线的最短距离小于设定的容差TOL相比于传统的三次样条逼近算法,本算法1. 能处理带噪声的数据集,曲线更加光滑2. 所需的控制点更少

query多选下拉框插件 jquery-multiselect-程序员宅基地

文章浏览阅读106次。效果:http://www.erichynds.com/examples/jquery-ui-multiselect-widget/demos/特性支持点击label实现checkbox组选择.头部选项,如全选/ 取消全选 /关闭功能.支持键盘选择.支持5种不同的事件回调函数.以列表方式显示选中项目,并且可以设置最大显示值.方便改变位置,渐变速度,滚动..._multiselect 多级 多选插件 checkbox

Lombok简介、使用、工作原理、优缺点_lombok的优缺点-程序员宅基地

文章浏览阅读1.4k次。一、Lombok简介官方介绍Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your_lombok的优缺点

前端进阶之路-程序员宅基地

文章浏览阅读204次。前端进阶之路思维导图地址https://zh.javascript.info/红宝书:JavaScript高级程序设计绿宝书:JavaScript语言精髓与编程实践黄宝书:你不知道的JavaScript(上/中)蓝宝书:JavaScript设计模式与开发实践react工程师修炼指南JS DOM编程艺术(第2版):看红宝书的前置书,因为比较简单min-vue源码didact源码算法第四版http权威指南迷你书籍: DOM启蒙Ja

Java中解决乐观锁中ABA经典问题_java 锁 aba-程序员宅基地

文章浏览阅读397次。packagecom.yellow.cas;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;importjava.util.concurrent.atomic.AtomicStampedReference;public classABATest3 { publi..._java 锁 aba