前端接收后端数据时,可能会遇到精度丢失的问题

  • 前言
  • 问题原因
  • 如何解决
  • 解决示例
    • 后端解决方式
    • 前端进行处理

前言

之前项目开发过程中遇到过前端接收后端数据时,遇到精度丢失的问题。当时进行了问题记录,本篇博客针对于这个问题进行问题原因并进行多种方式解决这个问题。

问题原因

前端接收后端返回的数据时出现精度丢失问题,通常是因为在数据传输过程中,数据类型被转换为了 JavaScript 中的 Number 类型,而 JavaScript 的 Number 类型有一定的精度限制,无法精确表示一些较大或较小的数值,从而导致精度丢失。

JavaScript 中的 Number 类型可以表示的数字范围是 -9007199254740991 到 9007199254740991,即在 2 的 53 次方以内的整数和小数,超过这个范围的数字可能会失去精度或被表示为特殊的 Infinity 或 NaN 值。这是由于 JavaScript 中的 Number 类型采用的是 IEEE 754 标准的双精度浮点数表示法,使用 64 位二进制格式存储数字,其中 1 位符号位、11 位指数位和 52 位尾数位。因此,JavaScript 中的 Number 类型能够存储的有效数字位数为 52 位,即最多可以精确表示 15 位十进制数字。

如何解决

  1. 使用字符串类型传递:将后端生成的 ID 转换成字符串类型后再传递给前端,这样可以避免浮点数精度丢失的问题。

  2. 在前端进行精度处理:在前端接收到 ID 后,可以使用一些 JavaScript 库来进行高精度计算和处理,例如 decimal.js、bignumber.js 等,这样可以避免浮点数精度丢失的问题。

  3. 前后端统一使用相同的算法:可以尝试使用前后端都支持的算法来生成全局唯一 ID,这样可以保证前后端生成的 ID 是一致的,避免精度丢失的问题。

需要注意的是,如果精度丢失的问题比较严重,可能会影响到系统的正确性,因此需要根据实际情况选择适合的解决方法。

解决示例

后端解决方式

在对应的实体属性上添加注解:

@JsonSerialize(using = com.fasterxml.jackson.databind.ser.std.ToStringSerializer.class)

@JsonSerialize 注解用于指定在将 Java 对象序列化为 JSON 字符串时使用的序列化器,其中
using 属性用于指定序列化器的实现类。在这个例子中,
using 属性指定为
com.fasterxml.jackson.databind.ser.std.ToStringSerializer.class,即使用 Jackson 序列化库中的
ToStringSerializer 类来将 Java 对象的值序列化为字符串类型的 JSON 值。

具体来说,在这个例子中,ToStringSerializer 类实现了将 Java 对象的值序列化为字符串类型的 JSON 值的功能。例如,如果 Java 对象的一个属性是 Long 类型,使用默认的序列化方式时,该属性的值会被序列化为一个数字类型的 JSON 值。但是使用 ToStringSerializer 类时,该属性的值会被序列化为一个字符串类型的 JSON 值,即将该属性的值转换为字符串类型后再进行序列化。
使用 ToStringSerializer 类的主要作用是将 Java 对象中的某些属性序列化为字符串类型的 JSON 值,这通常用于解决一些精度丢失或溢出等问题。例如,在 Java 中,当使用 double 或 float 类型来表示一个非常大或非常小的数时,可能会出现精度丢失或溢出的问题,这时可以使用 ToStringSerializer 类将该属性的值转换为字符串类型后再进行序列化,避免精度问题。

示例:

@JsonSerialize(using = com.fasterxml.jackson.databind.ser.std.ToStringSerializer.class)
//解决雪花算法生成后,前端接收后缺失精度问题
private BigInteger id;

前端进行处理

将数字121212121211212564(17位)使用 decimal.js 库在前端进行精度处理的示例:

  1. 安装 decimal.js 库:
npm install decimal.js
  1. 在前端代码中引入 decimal.js:
import Decimal from 'decimal.js';
  1. 使用 Decimal 类来进行精度处理:
const num = '121212121211212564';
const decimalNum = new Decimal(num);
console.log(decimalNum .toString()); // '121212121211212564'

在这个示例中,我们使用 Decimal 类将给定的数字 121212121211212564 转换成了 decimal.js 支持的数据类型,然后使用 toString() 方法将结果转换成字符串类型。
需要注意的是,如果你给出的数字有小数部分,那么你需要使用 toDecimalPlaces() 方法来指定小数部分的精度。如果你不使用 toDecimalPlaces() 方法,那么在进行运算时,可能会出现精度丢失或者溢出的问题。

发表回复