js小数的数学运算和四舍五入精度问题

本文探讨JavaScript中由于数字存储和计算方式导致的小数精度问题,分析原因并提出解决方案。通过将小数转换为整数进行运算,避免精度损失。同时,文章指出这种方法的局限性,并讲解了四舍五入保留小数位的正确方法,以解决常见的四舍五入问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在开发中,要进行计算,你可能会遇到小数运算,运气好的话,你的测试测不到精度问题,但其实这是很严重的,以下两个典型例子先感受以下

0.1 + 0.2 = 0.30000000000000004

35.41 * 100 = 3540.9999999999995

是不是出乎你的意料?

写这篇文章的原因是网上找了些资料,要不就是介绍不全的,要不就是存在错误的(可能大家没发现),要不就是方案还有待加强的。于是我决定自己整理出一份较为全面而不误导别人的文章出来(文章方案对网上大部分资料存在的缺陷进行弥补增强),如果您发现不足,请告诉我,虚心请教。

以下我们了解原因以及寻找解决方案。

原因

js的数字存储情况

在计算机中存储的信息都是二进制来表示,我们都知道,js中数字类型只有Number,不像其他语言如javaintdouble类型等。它的实现遵循 IEEE 754 标准,使用64位固定长度来表示,也就是标准的 double 双精度浮点数。

其中这64位数又分为三部分(从左往右看):

  • 符号位:第一位为符号位,0表示正数,1表示负数;
  • 指数位:中间的11位存储指数,用来表示次方数
  • 尾数位:最后的52位就是尾数,超出部分会0舍1入(类似四舍五入)

计算过程

以例子0.1 + 0.3展开说明:

  1. 先把0.1和0.3转化位二进制,会发现转化后的二进制会陷入循环,超出了上面说的尾数52位,因为小数位只能到52位,所以进行0舍1入的处理,0.1 -> 0.0001100110011001100110011001100110011001100110011010; 0.2 -> 0.0011001100110011001100110011001100110011001100110011
  2. 转化后的二进制进行加法运算,得0.0100110011001100110011001100110011001100110011001101,转化为十进制就是0.30000000000000004

于是就这样,得到了一个出乎你预料的结果了。并不是所有小数计算都会这样,是一些小数转化位二进制时出现超出52位数时,就可能会出现这种意外结果。

解决方案

上面我们知道了出现意外结果是因为小数转化为二进制时发生问题,那整数呢?很自然,整数也是有最大值安全值的,就是2的53次方,为9007199254740992。超出这个数值的计算同样也是带来精度问题。但是我们一般使用是不会超出这个值的(如果你的需求也要考虑超出这个数值,抱歉,我无能为力)

如果你的需求可以无视上面整数最大安全值的弊端,那么接下来的解决方案才是适合你的。

解放方案:把小数运算中的小数,升级转化为整数(乘以10的n次幂),在进行运算,将最后结果再降级(除以10的n次幂)

上面的描述仅仅是一个思路,一个转化思路。怎么将小数转化为整数,这就讲究了,不能真的进行乘法计算,乘以10的n次幂,还记得前言里的第二个例子吗,这种转化整数的方式本身就是一个小数运算,所以还是会出现问题。

我们要用字符串替换的方式来实现这个“升级”:

原值:1.23

转化过程:
1. 化为字符串 '1.23'
2. '1.23'.replace('.', ''),得'123',相当于乘以10的2次幂

结合实际例子来了解大概的一个情况,例子1.1 + 1.22

1. 分别对1.1和1.22进行字符串替换,变成'110'和'122',相当于乘以10的2次幂
2. 对替换结果转化回数字类型然后再进行加法运算:110 + 122 = 232
3. 对232除以10的2次幂,得2.32

以上就是一个转化和运算过程。

基于上述基本思想,我对加减乘除进行一个方法封装,方便大家进行小数运算。

/**
 * 带有小数的加法/减法运算
 * 减法实际上可看成加法,所以如果要做减法,只需第二个参数即被减数传负值即可
 * @param {Number} arg1 - 加数/减数
 * @param {Number} arg2 - 加数/被减数
 */
function addFloat(arg1, arg2) {
   
    let m = 0; // 记录两个加数中最长的小数位长度
    let arg1Str = arg1 + '';
    let a
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值