记录--前端金额运算精度丢失问题及解决方案

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

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--前端金额运算精度丢失问题及解决方案

前言

前端开发中难免会遇到价格和金额计算的需求,这类需求所要计算的数值大多数情况下是要求精确到小数点后的多少位。但是因为JS语言本身的缺陷,在处理浮点数的运算时会出现一些奇怪的问题,导致计算不精确。

本文尝试从现象入手,分析造成这一问题原因,并总结和整合一些通用的解决方案,以供大家参考。

现象回顾

下面的是JS进行数值运算过程中常见的问题,这个问题有个专业的名称叫精度丢失

在 JavaScript 中整数和浮点数都属于 Number 数据类型,所有的数字都是以 64 位浮点数形式存储,整数也是如此。所以我们在打印 1.00 这样的浮点数的结果是 1 而非 1.00 。在一些特殊的数值表示中,例如金额,这样看上去有点别扭,但是至少值是正确了。然而要命的是,当浮点数做数学运算的时候,你经常会发现一些问题,举几个例子:

// 加法
0.1 + 0.2 = 0.30000000000000004
0.7 + 0.1 = 0.7999999999999999
0.2 + 0.4 = 0.6000000000000001
2.22 + 0.1 = 2.3200000000000003

// 减法
1.5 - 1.2 = 0.30000000000000004
0.3 - 0.2 = 0.09999999999999998

// 乘法
19.9 * 100 = 1989.9999999999998
19.9 * 10 * 10 = 1990
1306377.64 * 100 = 130637763.99999999
1306377.64 * 10 * 10 = 130637763.99999999
0.7 * 180 = 125.99999999999999
9.7 * 100 = 969.9999999999999
39.7 * 100 = 3970.0000000000005

// 除法
0.3 / 0.1 = 2.9999999999999996
0.69 / 10 = 0.06899999999999999

问题分析

JavaScript 里的数字是采用 IEEE 754 标准的 64 位双精度浮点数。

该规范定义了浮点数的格式,对于 64 位的浮点数在内存中的表示,最高的 1 位是符号位,接着的 11 位是指数,剩下的 52 位为有效数字,具体对应如下:

  • 第 0 位:符号位, s 表示 ,0 表示正数,1 表示负数;
  • 第 1 位到第 11 位:储存指数部分, e 表示 ;
  • 第 12 位到第 63 位:储存小数部分(即有效数字),f 表示,

符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。

IEEE 754 规定,有效数字第一位默认总是 1,不保存在 64 位浮点数之中。也就是说,有效数字总是 1.xx…xx 的形式,其中 xx..xx 的部分保存在 64 位浮点数之中,最长可能为 52 位。

因此,JavaScript 提供的有效数字最长为 53 个二进制位(64 位浮点的后 52 位 + 有效数字第一位的 1)。

那么,JavaScript 在计算 0.1 + 0.2 时,到底发生了什么呢?

首先,十进制的 0.10.2 都会被转换成二进制,但由于浮点数用二进制表达时是无穷的,就成了下面的样子。

0.1 -> 0.0001100110011001...(无限)
0.2 -> 0.0011001100110011...(无限)
IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持 53 位二进制位,所以两者相加之后得到的二进制就是下面的样子。
0.0100110011001100110011001100110011001100110011001100

最终,因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了 0.30000000000000004,所以在进行算术计算时就产生了误差。

解决方案

通常情况下对计算精度要求高的业务场景都应该交给后端去计算和存储,因为后端有成熟的方案和工具来解决这种计算问题。

这里是对网上常见的几个解决方案的汇总和整理,但是方案一和方案二都存在一定的局限性,我会在对应的方案里进行说明。通常我们前端做这类运算通常只用于表现层,所以这两个方案基本是够用的。

方案一

在应对确定精度浮点数运算时,可以把金额转换成整数进行运算,最后再将结果转换成真实金额。

// 定义金额(保留两位小数)
const amount1 = 0.10;
const amount2 = 0.20;

// 转整数运算,并还原成真实金额
const result = ((amount1 * 100) + (amount2 * 100)) / 100;

// 结果
console.log(result); // 0.3

// 直接运算
console.log(amount1 + amount2); // 0.30000000000000004

需要注意的是上面的例子只能处理最多两位小数的运算场景,如果小数位不确定这个方法是行不通的。

方案二

JavaScript 内置的 toFixed() 方法可以将数字转换成保留指定小数位的字符串。这个方法适用于简单的金额计算。但需要注意舍入误差,因为转换后是字符串,失去了浮点数的特性,最后的结果坑你存在微小的误差。

// 定义金额
const amount1 = 0.1;
const amount2 = 0.2;

// 加法运算
const result = (amount1 + amount2).toFixed(2); // 保留两位小数

// 注意这里运算的结果应该是:0.30000000000000004
console.log(result); // 输出 "0.30"

toFixed 它是一个四舍六入五成双的诡异的方法(也叫银行家算法)。"四舍六入五成双"含义:对于位数很多的近似数,当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位,这种修约(舍入)规则是“四舍六入五成双”,也即“4舍6入5凑偶”这里“四”是指≤4 时舍去,"六"是指≥6时进上,"五"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:5前为奇数,舍5入1;5前为偶数,舍5不进(0是偶数)。

第三方库

现代前端发展至今,已经有很多成熟的类库来帮助我们解决此类问题,这类类库通常有很好的通用性和兼容性。

下面我将推荐几个人气较高的数字计算类库。

Math.js

Math.js 是专门为 JavaScript 和 Node.js 提供的一个广泛的数学库。它具有灵活的表达式解析器,支持符号计算,配有大量内置函数和常量,并提供集成解决方案来处理不同的数据类型像数字,大数字 (超出安全数的数字),复数,分数,单位和矩阵,功能强大,易于使用。

官网:mathjs.org/

GitHub:GitHub - josdejong/mathjs: An extensive math library for JavaScript and Node.js

decimal.js

为 JavaScript 提供十进制类型的任意精度数值。

官网:decimal.js API

GitHub:GitHub - MikeMcl/decimal.js: An arbitrary-precision Decimal type for JavaScript

big.js

Big.js 是一个用于处理任意精度的大数运算的 JavaScript 库。它解决了 JavaScript 中处理大数运算时精度丢失的问题,提供了更高精度的计算能力。

Big.js 库的特点包括:

  • 任意精度:Big.js 允许您处理任意精度的数字,而不受 JavaScript 内置数字类型的限制。
  • 高精度计算:Big.js 提供了精确的加法、减法、乘法、除法和取余等运算,以及比较和舍入等功能。
  • 可配置的精度和舍入规则:您可以自定义 Big.js 运算的精度和舍入规则,以满足特定的需求。
  • 支持链式操作:您可以使用链式调用来执行多个运算,使代码更简洁易读。
  • 适用于浏览器和 Node.js:Big.js 可以在浏览器和 Node.js 环境中使用,兼容性良好。

Big.js 库非常适用于需要高精度计算的场景,如金融、密码学、科学计算和大数据处理等。它允许开发人员在 JavaScript 中进行准确的数字计算,避免了精度损失带来的问题。

官网:big.js API

GitHub:GitHub - MikeMcl/big.js: A small, fast JavaScript library for arbitrary-precision decimal arithmetic.

总结

本文对 Javascript 中浮点数运算出现的精度丢失问题进行了还原,分析了问题产生的原因在于二进制本身。同时给出了三个网络上比较成熟的解决方案,其中第一和第二方案基本可以满足大部分开发场景,如果不能满足就使用类库。

本文转载于:

https://juejin.cn/post/7325627704782307337

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--前端金额运算精度丢失问题及解决方案文章来源地址https://www.toymoban.com/news/detail-825092.html

到了这里,关于记录--前端金额运算精度丢失问题及解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 后端传long类型数据到前端精度丢失问题

    在 Spring Boot 中,将 long 类型传输到前端时,会发现该类型的值可能会出现精度丢失的问题。 这是因为在 JavaScript 中,数字类型默认会被转换为双精度浮点数,而双精度浮点数的精度有限,只能精确表示 2 的 53 次方以内(即 Number.MAX_SAFE_INTEGER,约为 9 x 10^15)的整数。对于超过

    2024年02月15日
    浏览(29)
  • 前端接收后端数据时,遇到精度丢失的问题

    之前项目开发过程中遇到过前端接收后端数据时,遇到精度丢失的问题。当时进行了问题记录,本篇博客针对于这个问题进行问题原因并进行多种方式解决这个问题。 前端接收后端返回的数据时出现精度丢失问题,通常是因为在数据传输过程中,数据类型被转换为了 JavaScr

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

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

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

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

    2024年02月16日
    浏览(34)
  • SpringBoot解决前端js处理大数字丢失精度问题Long转String

    一、Jackson对Long类型的处理导致精度丢失的问题 表的某一个字段的类型是 BIGINT,对应的 Java 类的属性的类型就是 Long。当这个字段的值由后端返回给前端网页时,发现了精度丢失的问题。比如后端返回的值是 588085469986509185,到了前端是 588085469986509200,后面的几位数变成了

    2024年02月21日
    浏览(29)
  • js浮点数四则运算精度丢失以及toFixed()精度丢失解决方法

    1、四则运算精度丢失: 2、toFixed() 四舍五入精度丢失: js采用64位浮点数表示法(几乎所有现代编程语言所采用),这是一种二进制表示法。二进制浮点数表示法并不能精确表示类似 0.1 这样简单的数字。 这个问题不只在js中才会出现,在任何使用二进制浮点数的编程语言中

    2024年02月05日
    浏览(38)
  • js运算精度丢失

    目录 问题所在:  解决方法1(低精度): toFixed介绍: 代码实现: 小结: 解决方法2(高精度): 代码实现: 小结: 当两个数包含小数进行运算的时候结果并不是正确的结果,而是出现了精度丢失的情况(小数点后面出现很多位)。 界面显示: 在运算结果的后面加上toFi

    2023年04月26日
    浏览(27)
  • 前端处理后端传来的Long型数据精度丢失的问题--对象转换器Jackson0bjectMapper

    1、问题描述 前端提交参数 后端处理 前端js对long型数据进行处理时丢失精度(只能保证16位),导致提交的id和数据库中的id不一致。 2、 问题实现 如何解决这个问题? 我们可以在服务端给页面响应json数据时进行处理,将long型数据统一转为String字符串 3、具体代码实现–对象

    2024年02月16日
    浏览(32)
  • spring Boot处理返回给前端Long类型精度丢失

    项目中采用springcloud Alibaba技术开发分布式系统,开发过程中采用雪花算法生成分布式Id,为Long类型,而Long类型返回给前端,会出现精度丢失问题。 接下来我们主要了解下,如何快速的处理精度丢失的问题 可以直接在返回实体属性添加\\\"@JsonSerialize(using = ToStringSerializer.class)\\\"。

    2024年02月02日
    浏览(36)
  • 记录--解决前端内存泄漏:问题概览与实用解决方案

    内存泄漏是前端开发中的一个常见问题,可能导致项目变得缓慢、不稳定甚至崩溃。在本文中,我们将深入探讨在JavaScript、Vue和React项目中可能导致内存泄漏的情况,并提供详细的代码示例,以帮助开发人员更好地理解和解决这些问题。 1. 未正确清理事件处理器 JavaScript中的

    2024年02月11日
    浏览(33)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包