全链路精度问题解决方案

5 阅读3分钟

一、数据库层:精准存储,奠定基础

总体原则,不要出现和使用浮点数,确定整型用Int或者BigInt(比如ID号之类),不确定金额统一用Decimal类型。使用DECIMAL(p, s)类型,DECIMAL十进制高精度数值类型,专门用于存储需要精确计算的小数,不会像FLOAT/DOUBLE那样存在二进制浮点误差。

参数含义:p表示「总有效位数」(整数位 + 小数位),s表示「小数位数」。例如:表示普通金额DECIMAL(18, 2)

一些注意细节:

金额:一般保留2位(JPY币种特殊,保留0位小数),小数位可以补0,例如:"1.00"

汇率:前端展示保留5位小数,数据库存储保留9位小数,看业务要求。

二、后端层:高精度计算,业务闭环

接收前端参数时,优先接收「字符串格式」的金额,避免传输过程中的精度丢失;

计算时使用专门的高精度数值类型,杜绝普通浮点数(double/float);

与数据库交互时,直接映射DECIMAL类型到后端高精度类型,无需额外转换;

所有金融计算完成后,统一格式化保留指定小数位(通常2位),避免多余小数位流转。

三、 前后端交互:原样传输,避免失真

前端传参数给后端,核心方案是JSON中金额字段统一用字符串

后端返回给前端 

后端将高精度类型(BigDecimal/Decimal)转换为「字符串」后,放入 JSON 响应体返回。

一些注意细节:

不要直接返回BigDecimal对象(部分 JSON 序列化框架会将其转为double,导致精度丢失),需主动转为字符串。

四、前端层:精准展示,安全操作

前端负责金额的输入、展示和简单处理(不负责核心计算,核心计算必须放在后端),需要保证用户操作和页面展示的精度准确,这里以JavaScript/TypeScript为例提供方案。

  1. 不使用Number类型处理金额,避免精度丢失;
  2. 核心计算(如金额拆分、手续费计算)不放在前端,前端仅做「格式校验、展示、传递」;
  3. 引入第三方高精度库处理前端必要的简单金额操作(如金额格式化、相加展示)。

统一使用decimal.js库,查看源代码,核心几个细节:

  1. 该库的rounding mode 默认 ROUND_HALF_UP,既四舍五入。和后端是一致的。结合业务
Decimal(a).add(b).toFixed(2)
  1. 如果特殊业务需求,需要向上取整或者向下取整,使用ROUND_UP或者ROUND_DOWN,即
Decimal(a).add(b).toFixed(2, Decimal.ROUND_UP) 
Decimal(a).add(b).toFixed(2, Decimal.ROUND_DOWN) 
  1. 使用该库计算不区分字符串和数字,库里面有内部处理。

  2. 使用该库,推荐使用链式调用,清晰优雅

  3. 使用该库,原型方法对照如下,推荐使用缩写,简洁优雅

/*

   *  absoluteValue             abs

   *  ceil

   *  clampedTo                 clamp

   *  comparedTo                cmp

   *  cosine                    cos

   *  cubeRoot                  cbrt

   *  decimalPlaces             dp

   *  dividedBy                 div

   *  dividedToIntegerBy        divToInt

   *  equals                    eq

   *  floor

   *  greaterThan               gt

   *  greaterThanOrEqualTo      gte

   *  hyperbolicCosine          cosh

   *  hyperbolicSine            sinh

   *  hyperbolicTangent         tanh

   *  inverseCosine             acos

   *  inverseHyperbolicCosine   acosh

   *  inverseHyperbolicSine     asinh

   *  inverseHyperbolicTangent  atanh

   *  inverseSine               asin

   *  inverseTangent            atan

   *  isFinite

   *  isInteger                 isInt

   *  isNaN

   *  isNegative                isNeg

   *  isPositive                isPos

   *  isZero

   *  lessThan                  lt

   *  lessThanOrEqualTo         lte

   *  logarithm                 log

   *  [maximum]                 [max]

   *  [minimum]                 [min]

   *  minus                     sub

   *  modulo                    mod

   *  naturalExponential        exp

   *  naturalLogarithm          ln

   *  negated                   neg

   *  plus                      add

   *  precision                 sd

   *  round

   *  sine                      sin

   *  squareRoot                sqrt

   *  tangent                   tan

   *  times                     mul

   *  toBinary

   *  toDecimalPlaces           toDP

   *  toExponential

   *  toFixed

   *  toFraction

   *  toHexadecimal             toHex

   *  toNearest

   *  toNumber

   *  toOctal

   *  toPower                   pow

   *  toPrecision

   *  toSignificantDigits       toSD

   *  toString

   *  truncated                 trunc

   *  valueOf                   toJSON

   */