如何避免和解决JavaScript中精度损失问题

简介: 如何避免和解决JavaScript中精度损失问题

前言🏇🏇


在参加蓝桥杯的时候遇到了一个题目,这个题目考察了js中的精度损失问题,但是由于自己学艺不精,此前只是有所耳闻,但是从来没有去了解过如何解决,导致被这个点所困住而没有解出题目。


什么是精度损失?🍉🍉


比如说两个小数在相减或者相加造成结果和实际值有偏差,得到了一个和结果很接近但是不是想要得到的值的情况。

console.log(14.55-12.11)//结果是2.4400000000000013

比如正常情况下比如14.55-12.11结果是2.44,但是由于JavaScript是一门弱类型的语言,运算精度丢失的一个主要原因是浮点数的不精确性会造成精度的损失,使结果变成2.4400000000000013。

那么造成精度损失的原因都有那些呢?

  1. 有限的二进制表示位数:JavaScript 中的浮点数使用 64 位表示,其中一部分用于表示指数部分和符号位,而另一部分用于表示小数部分。由于有限的位数,无法精确表示所有的十进制数,特别是那些不能被二进制精确表示的分数,例如 0.1 或 0.2。
  2. 舍入误差:在进行浮点数运算时,由于位数的限制,可能会出现舍入误差。这是因为某些十进制数在二进制表示中可能是无限循环的,而在转换为有限的二进制表示时,会进行舍入操作,从而引入了误差。


如何解决精度损失呢?💬💬


由于JavaScript是一门弱类型的语言,那么不想C,Java可以有专门的方式解决,我们最好的解决方式就是避免使用小数进行运算

  • 如果我们要进行与金额有关的运算的时候,我们就可以先将位数扩大,讲小数转换为整数进行相关的运算,最后再除以相应的扩大的倍数即可。这样就可以有效的避免直接使用小数进行运算。
  • 限制小数的位数,我们在js中可以使用toFixed方法来限制小数的位数,比如我们可以使用toFixed(2)将结果保留两位小数。

但是在JavaScript这些方法还是无法彻底的避免精度的损失问题只能尽量的避免,如果涉及到了对精度要求很高的计算,那么还是需要使用第三方的包bignumber.jsdecimal.js 来解决。


分享一下蓝桥杯题目⭐️⭐️


这个题目主要是讲的什么呢?其实就是我们最常用的红包功能:

输入红包的总金额和要发红包的个数,点击确定之后要求生成一个数组,数组中的每个元素代表每个红包的金额,要求每个红包的金额最低不能低于0.01元当然每个红包最多保留两位小数,最终这些每个红包的金额加起来要等与红包的总金额

废话不多说,直接上代码

这部分代码是实现题目核心功能的代码,我将代码拆分成几部分来讲

function generateRedPacket(totalMoney, totalAmount) {
  // 将金额转换为以分为单位,避免小数直接运算
    let MoneyTopoints = Math.round(totalMoney * 100);
       // 初始化红包数组
    let redpacket = [];
    .....//核心代码部分
   return redpacket;  
}
  • 首先是第一部分代码,这部分代码就是进行了一个初始化的功能,首先将传递的totalMoney转换为分为单位,这样如果传递的数据有小数存在就会被转换为整数,使用了Math.round方法,这个方法相当于是四舍五入的功能会将小数转换为最近的整数,比如可以将10.5转换为11,将11.4转换为11,在函数的最后将的到的红包数组return回去
// 当前能分配的红包总金额 
let maxRedpacket = MoneyTopoints;
    for (let i = 0; i < totalAmount; i++) {
       ....//特殊条件的判断的部分
// 生成随机的红包金额,并且将值转换为数字类型
 let randomRedpacket = Math.floor((maxRedpacket * Math.random()) / (totalAmount - i));
 // 如果小于0.01元,那么重新生成,这里是转换成分为单位了,所以小于1
      while (randomRedpacket < 1) {
        randomRedpacket = Math.floor(
          (maxRedpacket * Math.random()) / (totalAmount - i)
        );
    }
     ... ...//待补充部分
 }
  • 这部分代码就是比较核心的代码了,定义一个当前能分配的红包的最大金额,每次分出一个红包这个金额就要减少,然后我们需要生成随机的金额数目,通过maxRedpacket * Math.random() 这部分代码实现,但是为什么后面还要除以totalAmount - i 呢?
  • 之所以这么做是因为题目要求不能使每次分出去的红包金额少于0.01,由于随机数具有不确定性,当maxRedpacket的值很大时候,有可能此时很出去的红包很大,造成后面的红包太小不符合要求,这样下面的while代码就会陷入死循环中,无论如何分红包还是小于1分,这样就会使程序卡死无法继续执行,因此为了避免单次分出去的红包过大,就做了这个处理。
  • 其次Math.floor(x)这个方法的功能就是向下取整,会返回一个小于x的最大整数。
  • 然后就是一个简单的判断,如果此次分出去的红包不符合要求,就要重新的进行红包的分配,直到满足要求为止。
   redpacket.push(Number((randomRedpacket / 100).toFixed(2)));
     MoneyTopoints = MoneyTopoints - randomRedpacket;
  • 紧接着在生成红包的金额数之后,我们还要进一步的处理,首先我们要/100是金额重新的转换为元,然后使用toFixed方法保留两位小数,但是注意,在调用toFixed之后会返回一个字符串,因此我们要使用Number()将其重新的转换为数字型的数据,然后就是使用push方法将数据存进实现定义的数组redpacket中。
  • 然后我们要对1MoneyTopoints进行处理,对当前的最大金额进行处理,需要减去当前已经生成的红包金额,注意这里我们就巧妙的避免了直接使用小数来进行加减,有效的避免了js中的精度损失问题,这便是我们开始说的方法之一,

按目前来说程序好像已经没有什么问题,但是其实还是有问题的。

因为当程序执行到最后一个红包的时候,还是会执行maxRedpacket * Math.random() 进行随机数的相乘,这样就会使最后的到的所有红包金额加起来是不等于红包的总金额的,这显然是不对的,正确的做法其实这个时候maxRedpacket就是最后一个红包的金额。应该直接进行赋值。

   if (i === totalAmount - 1) {
        let randomRedpacket = maxRedpacket;
        redpacket.push(Number((randomRedpacket / 100).toFixed(2)));
        break;
      }
  • 所以我在开头加上了一个判断条件判断是否是最后一个红包的分配,如果是最后一个红包的分配,那么就直接进行赋值,然后将值追加到数组当中直接break结束循环。


预览效果🏋️‍♀️🏋️‍♀️


image.png

大家可以自己加一下会发现最终结果等于输入值即为30


总结


在蓝桥杯考试中发现了很多自己的不足之处,还有很多需要提高的部分,一些基础的知识掌握的不到位,所以说学习不能只停留在表面,还是要深入的学习,这样才是真正的学会和掌握。

相关文章
|
6月前
|
存储 缓存 JavaScript
请描述一种JavaScript内存泄漏的情况,并说明如何避免这种情况的发生。
JavaScript内存泄漏常由闭包引起,导致无用对象滞留内存,影响性能。例如,当一个函数返回访问大型对象的闭包,即使函数执行完,对象仍被闭包引用,无法被垃圾回收。防止泄漏需及时解除引用,注意事件监听器清理,使用WeakMap或WeakSet,定期清理缓存,以及利用性能分析工具检测。
38 2
|
6月前
|
JSON JavaScript 前端开发
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
557 0
|
6月前
|
存储 缓存 JavaScript
如何避免 JavaScript 中的内存泄漏?
如何避免 JavaScript 中的内存泄漏?
|
6月前
|
存储 前端开发 JavaScript
【JavaScript】浮点数精度问题
浮点数精度问题是指在计算机中使用二进制表示浮点数时,由于二进制无法精确表示某些十进制小数,导致计算结果可能存在舍入误差或不精确的情况。 这个问题主要源于浮点数的存储方式。在计算机中,浮点数通常使用IEEE 754标准来表示。该标准将浮点数分为符号位、指数位和尾数位,使用科学计数法来表示一个浮点数。
104 0
|
4月前
|
JavaScript
js 精确计算(解决js四则运算精度缺失问题)
js 精确计算(解决js四则运算精度缺失问题)
144 0
|
6月前
|
Web App开发 JavaScript 前端开发
JavaScript 性能优化:举例说明如何避免内存泄漏。
JavaScript 性能优化:举例说明如何避免内存泄漏。
55 1
|
6月前
|
存储 JavaScript 前端开发
浮点数不再神秘:JS浮点数精度详解
浮点数不再神秘:JS浮点数精度详解
|
6月前
|
存储 JavaScript 前端开发
说说JavaScript数字精度丢失的问题,如何解决?
在 JavaScript 中,数字精度丢失是一种普遍的问题。这是因为 JavaScript 内部的数字均以 IEEE 754 标准的双精度浮点数格式存储,这种格式只能精确表示有限个小数,而对于一些无限循环小数或无理数,无法精确表示,就会出现精度丢失的情况。
150 0
|
JSON JavaScript 前端开发
js精度丢失坑
js精度丢失坑
100 1