〇、js 的数值计算存在结果不精确的情况

最近接触财务相关系统,页面上会有一些简单的计算,就发现其实是非常简单的计算,但 js 计算出来的结果却不是预期值,可能带上一大串 0 或 9,导致计算结果错误,本文来简单汇总下,以及如何处理来避免这个问题。

先看看都会有哪些不精确的情况。

// 【加减乘除 四种基础运算】
// 精度问题其实也不是全都会有,比如以下测试:
let num1 = 0.1;
let num2 = 0.1;
let result = num1 + num2;
console.log("0.1+0.1 = " + result);
// 0.1+0.1 = 0.2
// 0.1+0.2 = 0.30000000000000004
// 1.1*1.2 = 1.32
// 1.1*1.1 = 1.2100000000000002
// 其实四种基础运算都会出现类似情况,这里就简单列两类 // 【数值比较】
console.log("'0.1+0.2 = 0.3' 比较结果:", 0.1 + 0.2==0.3);
// '0.1+0.2 = 0.3' 比较结果: false

一、原因简介

因为计算机内部的信息都是由二进制方式表示的,即 0 和 1 组成的各种编码,但由于某些浮点数没办法用二进制准确的表示出来,也就带来了一系列精度问题。当然这也不是 js 独有的问题。

推荐一个文章吧,有兴趣可以深究下: https://zhuanlan.zhihu.com/p/33333351 ,讲的应该是很详细,但有些复杂博主看了一遍也不是太了解。

下面把大概的流程梳理下。

  • 将小数转成二进制数

小数部分乘 2 取整数部分,若小数不为 0 则继续乘 2,直至小数部分为 0。然后将取出的整数位正序排列。整数部分就除以 2。

0.1 * 2 = 0.2   // 取0
0.2 * 2 = 0.4 // 取0
0.4 * 2 = 0.8 // 取0
0.8 * 2 = 1.6 // 取1
0.6 * 2 = 1.2 // 取1
0.2 * 2 = 0.4 // 取0
......

  0.1 的二进制表示是:0.000110011......0011.....(0011无限循环)
  0.2 的二进制表示是:0.00110011......0011......(0011无限循环)

虽然是无限长度,但是还是得有一个标准的显示方式,那就是 IEEE 754 标准。

  • IEEE 754 标准方法表示数值

IEEE 754 标准是 IEEE 二进位浮点数算术标准(IEEE Standard for Floating-Point Arithmetic)的标准编号。IEEE 754 标准规定了计算机程序设计环境中的二进制和十进制的浮点数自述的交换、算术格式以及方法。

详细的标识法,就跳过了,直接来看结果。

js 只有一种数字类型 number,而 number 使用的是 IEEE 754 双精度浮点格式。最高位是一位符号位(0正 1负),后面的 11 位是指数,剩下的 52 位为尾数(有效数字)。

// js 中 0.1 的二进制存储格式为:
// 符号位用逗号分隔,指数位用分号分隔
0.1:0,01111111011;1001100110011001100110011001100110011001100110011010
0.2:0,01111111100;1001100110011001100110011001100110011001100110011010
  • 计算两个二进制数的和
// 二进制形式的结果
sum = 0.010011001100110011001100110011001100110011001100110100
// 最后再转成十进制
sum = 2^2 + 2^5 + 2^6 + ... + 2^52 = 0.30000000000000004440892098500626
// 近似后的结果:0.30000000000000004

详情可参考:https://zhuanlan.zhihu.com/p/33333351

二、解决方式

2.1 Math.Round() 函数,先乘后除 10 的 n 次方

根据需求的精度,先乘以 10 的 n 次方,通过 Math.Round() 函数取整后,再除以 10 的 n 次方。

function numToString(num){
let factor = Math.pow(10, 4); // 最多保留 4 位小数
// 通过乘以一个因子(例如 10 的 4 次方),然后四舍五入
// 最后,再除以相同的因子,可以实现对特定小数位数的精确控制
let roundedNum = Math.round(num * factor) / factor;
return roundedNum.toString();
}

关于 Math.Round() 四舍五入的规则,可以参考以下测试结果:

console.log("Math.round(4.2)     ", Math.round(4.2)  );
console.log("Math.round(4.5) ", Math.round(4.5) );
console.log("Math.round(4.7) ", Math.round(4.7) );
console.log("Math.round(-4.2) ", Math.round(-4.2) );
console.log("Math.round(-4.5) ", Math.round(-4.5) );
console.log("Math.round(-4.7) ", Math.round(-4.7) );
console.log("Math.round(1.5) ", Math.round(1.5) );
console.log("Math.round(2.5) ", Math.round(2.5) );
console.log("Math.round(3.5) ", Math.round(3.5) );
console.log("Math.round(-1.5) ", Math.round(-1.5) );
console.log("Math.round(-2.5) ", Math.round(-2.5) );
console.log("Math.round(-3.5) ", Math.round(-3.5) );

可以看出,正数的小数位为 5 时,进 1;负数小数位为 5 时,舍弃

2.2 toFixed() 方法,直接取小数点后固定位数

此方法就是直接指定要保留的几位小数,若小数位较少,就会以 0 补全

toFixed() 的四舍五入规则,并非严格的根据要保留小数位后边的小数来判断。

若作为判断的小数位为 5,且后边没有大于 0 的数,则舍入到最近的奇数;若 5 后边有非零的值,就直接进 1。

例如,4.55 保留 1 位小数,就是 4.5,但 4.5500001 的结果就是 4.6。

例如,-4.55 保留 1 位小数,就是 -4.5,但 -4.5500001 的结果就是 -4.6。

如下示例,供参考:

let num = 4.22;
console.log("num.toFixed(1):4.22) ", num.toFixed(1));
num = 4.55;
console.log("num.toFixed(1):4.55) ", num.toFixed(1));
num = 4.551;
console.log("num.toFixed(1):4.551) ", num.toFixed(1));
num = 4.65;
console.log("num.toFixed(1):4.65) ", num.toFixed(1));
num = 4.77;
console.log("num.toFixed(1):4.77) ", num.toFixed(1));
num = -4.22;
console.log("num.toFixed(1):-4.22) ", num.toFixed(1));
num = -4.55;
console.log("num.toFixed(1):-4.55) ", num.toFixed(1));
num = -4.551;
console.log("num.toFixed(1):-4.551) ", num.toFixed(1));
num = -4.65;
console.log("num.toFixed(1):-4.65) ", num.toFixed(1));
num = -4.77;
console.log("num.toFixed(1):-4.77) ", num.toFixed(1));
num = -4.77;
console.log("num.toFixed(1):-4.77) ", num.toFixed(4));

注意:toFixed() 的结果是字符串类型,若最终还需要 number 类型,就需要通过 Number() 函数进行转换。

2.3 通过正则表达式,多余位小数直接舍去

必须先将数字类型转换成字符串,再使用 match() 方法。

let num = 3.14959;
let numStr = num.toString(); // 必须为字符串
let fixedNumStr = numStr.match(/^-?\d+(\.\d{0,2})?/)[0]; // 两位小数的正则表达式
let fixedNum = parseFloat(fixedNumStr);
console.log(fixedNum); // 输出:3.14

js 数字计算的精度问题的更多相关文章

  1. JS数字计算精度误差的解决方法

    本篇文章主要是对javascript避免数字计算精度误差的方法进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助. 如果我问你 0.1 + 0.2 等于几?你可能会送我一个白眼,0.1 + 0. ...

  2. JS数字计算精度问题解决

    add(a, b) {//相加 var c, d, e; try { c = a.toString().split(".")[1].length; } catch (f) { c ...

  3. JS 浮点型计算的精度问题 推荐的js 库 推荐的类库 Numeral.js 和 accounting.js

    推荐的类库 Numeral.js 和 accounting.js 文章来自 http://www.css88.com/archives/7324#more-7324

  4. 关于js浮点数计算精度不准确问题的解决办法

    今天在计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前就一直碰到这个问题,都是简单的使用tofixed方法进行处理一下,这对于一个程序员来说是及其不严谨的.因此在网上收集了一些处理浮点数精 ...

  5. JavaScript数字计算精度丢失的问题和解决方案

    一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加:0.1 + 0.2 != 0.3 // true,下图是firebug的控制台截图: 看看java的计算结果:是不是让你很不能接受 再来 ...

  6. js数字位数太大导致参数精度丢失问题

    最近遇到个比较奇怪的问题,js函数里传参,传一个位数比较大,打印arguments可以看到传过来的参数已经改变. 然后查了一下,发现确实是js精度丢失造成的.我的解决方法是将数字型改成字符型传输,这样 ...

  7. js浮点数计算问题 + 金额大写转换

    一 js浮点数计算问题解决方案: 1.使用 NumberObject.toFixed(num) 方法 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. 2.较精度计算浮点数 ...

  8. js浮点数计算(加,减)

    最近工作中经常遇到需要处理浮点型计算的问题,开始一直都在用把浮点数先乘以10的对应小数的位数的次方化成整数再去开始计算. 例如100.01+100.02,可以化成(100.01*100+100.02* ...

  9. js中如何取精度

    js中如何取精度 一.总结 一句话总结:其实round()函数去经度会有误差,直接用num.toFixed(2)简单方便. toFixed()方法会按照指定的小数返回数值的字符串表示.var num ...

  10. js小数加减乘除时精度不准确

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

随机推荐

  1. Linux内存占用分析的几个方法,你知道几个?

    0. 引言: 系统内存是硬件系统中必不可少的部分,定时查看系统内存资源运行情况,可以帮助我们及时发现内存资源是否存在异常占用,确保业务的稳定运行. 例如:定期查看公司的网站服务器内存使用情况,可以确保 ...

  2. js_for循环的错误

    本段代码实现的效果是遍历数组中的每个元素,给每个元素插入一个类名 for (var i = 0; i < dropdownLi.length; i++) { if(i == 1){ contin ...

  3. Windows 安装 OpenSSH

    使用命令行安装 安装 OpenSSH 服务端和客户端 在管理员终端下运行以下命令: # 检查 OpenSSH 可用性 Get-WindowsCapability -Online | Where-Obj ...

  4. 【Jmeter】之进行单接口批量压力测试

    目录: 一.安装Jmeter 二.接口压力测试 p.p1 { margin: 0; font: 14px ".PingFang SC"; color: rgba(17, 31, 4 ...

  5. Pipeline流水线通过git拉取Jenkinsfile报错 error: RPC failed; result=22, HTTP code = 404

    Pipeline流水线通过git拉取Jenkinsfile报错 error: RPC failed; result=22, HTTP code = 404 在学习共享库时使用通过git拉取jenkin ...

  6. 坑人的opencv安装

    我想捡起来C++,最近在看opencv,于是我想着一起吧. 但是我低估了这个小麻烦的魅力,曾经安装opencv c++版本就头秃,如今依然头秃.说明我没长进啊-- 折腾了两天,终于装上了. 其中最麻烦 ...

  7. CSS & JS Effect – 画三角形 Triangle

    前言 画三角形有什么用? 可以做这样的 Design 参考 5 Ways To Create A Triangle With CSS Border Triangle 用 border 做 三角形应该是 ...

  8. JAVA与.NET DES加密解密

    项目需要在两个系统间采用DES加密,一个系统为JAVA开发的,另外一个.Net开发的 在网上找了很多写法但加密出的数据两个系统都无法匹配, 在做了小修改以后终于可以用了,已经测试过 JAVA版本 im ...

  9. 多线程ExecutorService 的理解与使用

    原文链接:https://www.cnblogs.com/gxz-sw/p/6754476.html 接口 Java.util.concurrent.ExecutorService 表述了异步执行的机 ...

  10. U179915 关于分级火箭的一点理想化的计算

    题目地址 本题是一道疯狂推式子的玄学复杂度sb题. 解题思路 1.数学部分 ​ 首先假定已经将火箭分成了 \(n+1\) 级,记使用了 \(n\) 个分级器.记各级的开始时间点为: \[0=t_0&l ...