PAYATHON 2026

如何计算 Reprofiled Amount 的最后一笔付款

支付阿杰

结论

金额计算时,统一换算成“分”,再用整数运算。前三笔付款均为 106017.96,研究成本为 424071.85,最后一笔付款应为:

424071.85 - 106017.96 × 3 = 106017.97

toLocaleString() 只能格式化显示结果,无法修正 JavaScript 浮点数的精度误差,因此不能依赖它处理金额计算。

原因

JavaScript 的 Number 采用 IEEE 754 二进制浮点数。很多十进制小数无法用二进制精确表示,所以计算:

424071.85 - (106017.96 * 3)

可能得到:

106017.96999999997

业务金额并没有减少,这是浮点数内部表示产生的误差。

formatAsCurrency() 返回的是格式化字符串。应先完成所有金额计算,再进行格式化,不能把用于显示的结果当作精确金额继续计算。

推荐方案:以“分”为单位计算

function toCents(amount) {
  return Math.round(Number(amount) * 100);
}

function formatCents(cents) {
  return (cents / 100).toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2
  });
}

const RC = 424071.85;
const numAmounts = [106017.96, 106017.96, 106017.96];

const rcCents = toCents(RC);

const paidCents = numAmounts.reduce(
  (total, amount) => total + toCents(amount),
  0
);

const remainingCents = rcCents - paidCents;
const remainingAmount = remainingCents / 100;

console.log(remainingAmount);             // 106017.97
console.log(formatCents(remainingCents)); // "106,017.97"

验证总额时,同样比较整数形式的“分”:

const allPaymentCents =
  paidCents + remainingCents;

console.log(allPaymentCents === rcCents); // true

如果最后一笔已存在,需要把差额补到最后一笔

如果数组已经包含全部季度付款,先把每笔金额换算成分,再将总额差额加到最后一笔:

const RC = 424071.85;
const numAmounts = [
  106017.96,
  106017.96,
  106017.96,
  106017.96
];

const rcCents = Math.round(RC * 100);
const paymentCents = numAmounts.map(amount => Math.round(amount * 100));

const currentTotalCents = paymentCents.reduce(
  (total, amount) => total + amount,
  0
);

const differenceCents = rcCents - currentTotalCents;

paymentCents[paymentCents.length - 1] += differenceCents;

const adjustedAmounts = paymentCents.map(cents => cents / 100);

console.log(adjustedAmounts);
// [106017.96, 106017.96, 106017.96, 106017.97]

最后再格式化显示:

const formattedAmounts = paymentCents.map(cents =>
  (cents / 100).toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2
  })
);

console.log(formattedAmounts);
// ["106,017.96", "106,017.96", "106,017.96", "106,017.97"]

注意事项

  • 计算金额时使用整数“分”,只有在展示时才除以 100。
  • toFixed() 和 toLocaleString() 用于格式化,不应承担账务精度控制。
  • 不要直接用 === 比较浮点金额的计算结果,应比较换算后的整数分值。
  • 如果金额可能超过 JavaScript 的安全整数范围,或涉及多币种、税率和更高精度,应使用 BigInt 或专用的十进制定点数库。
  • 这种方案适用于金额最多保留两位小数的情况。

备注:内容仅供参考。