JS 大数值处理和金额格式化处理方案

点赞 + 关注 + 收藏 = 学会了

在做前端开发或者使用 n8n、dify 等工具时可能会跟数字打交道,可能会遇到下面这些需求:

  • 显示金额:1234567.89 → 1,234,567.89
  • 金额计算:0.1 + 0.2
  • 超大 ID:9007199254740993
  • 阿拉伯数字转中文金额:123456.78 → 壹拾贰万叁仟肆佰伍拾陆元柒角捌分

很多刚接触 JavaScript 的开发者会直接使用 Number 类型处理这些问题,但实际上这里面隐藏了不少 坑。

举个例子:

如果后端传给你的 JSON 里的长 ID 没加引号,前端在 JSON.parse 的一瞬间就已经把精度丢了。

javascript 复制代码
const rawJson = '{"id": 9007199254740995}';
const parsed = JSON.parse(rawJson);
console.log(parsed.id.toString()); // "9007199254740996" 精度丢失了!!!

解决方案:

  1. 后端改 String:最省心的办法。让后端把超长 ID 以字符串形式下发。
  2. 前端插件 :如果后端不改,你可以使用 json-bigint 库来解析原始的 JSON 字符串。

数值是否安全?

在实际应用中,可以通过一些方法来胖段当前数值是否安全。

JS 提供的方法:

javascript 复制代码
Number.isSafeInteger(9007199254740991) // true
Number.isSafeInteger(9007199254740992) // false

处理大数值的几种方案

在 JavaScript 里,普通数字类型是 Number(64位双精度浮点),它有一个安全整数范围:

  • 最大安全整数:Number.MAX_SAFE_INTEGER = 9007199254740991
  • 最小安全整数:Number.MIN_SAFE_INTEGER = -9007199254740991

超过这个范围就会出现 精度丢失问题,例如:

javascript 复制代码
console.log(9007199254740991 + 1) // 9007199254740992
console.log(9007199254740991 + 2) // 9007199254740992  ❌ 精度丢失

如果你要处理 超过 Number 最大安全值的整数,有几种常见方案👇

方案1:BigInt

现代 JavaScript 提供了一种新的类型:BigInt。

它可以表示任意大的整数。

javascript 复制代码
const num = BigInt("9007199254740993")

console.log(num + 1n)
// 9007199254740994n

用 BigInt 的话,数字后面会跟着一个字母 n,看到它就能区分这个值和普通的 Number 类型不一样,这个需求可能会涉及很大的数值。

它还有一种简写方法,在赋值的时候不需要加引号括者数字,而是在数字后面加个 n。

ini 复制代码
const num = 9007199254740993n

需要注意的是,BigInt 不能和 Number 混合运算!!!

javascript 复制代码
1n + 1
// ❌ 报错

必须统一类型:

javascript 复制代码
1n + BigInt(1)

金融计算为什么不能直接用 Number

一个经典问题:

javascript 复制代码
0.1 + 0.2

# 结果是 0.30000000000000004

原因是浮点数精度问题。

金融系统一般有两种解决方案。

方案一:金额用"分"存储

例如:

javascript 复制代码
123.45 元

存储为:12345 分

前端展示时再除以 100。

javascript 复制代码
const amount = 12345 / 100

console.log(amount)
// 123.45

这种方式是比较老派的方法。

方案二:使用大数库

**MikeMcl 写了几个很出名的处理数字的JS库,比如:

我用 bignumber.js 演示一下。

安装 bignumber.js

bash 复制代码
npm install bignumber.js

在前端项目里引入:

javascript 复制代码
import BigNumber from 'bignumber.js';

此时直接计算小数位的数值

javascript 复制代码
const a = new BigNumber(0.1)
const b = new BigNumber(0.2)

a.plus(b).toString()
// 0.3

// 格式化,保留2位小数
a.plus(b).toFormat(2)
// 0.30

处理数值比较大的数据也没问题

javascript 复制代码
// 1. 创建大数(建议始终传入字符串)
const x = new BigNumber('9007199254740995.123456789');
const y = new BigNumber('100');

// 2. 加减乘除
const res = x.plus(y);      // 加
const res2 = x.minus(y);    // 减
const res3 = x.times(y);    // 乘
const res4 = x.div(y);      // 除

console.log(res.toString()); // "9007199254741095.123456789" (精度完全保留)

格式化数值

在金融行业,金额的展示不仅关乎美观,更关乎准确性 和合规性。针对你提出的千分位转换、中文大写转换以及大数处理

千分位格式化

方案1:toLocaleString()

JavaScript 原生支持国际化格式化。

javascript 复制代码
const amount = 123456789.56

amount.toLocaleString()
// 123,456,789.56

方案2:Intl.NumberFormat

金融项目比较推荐使用这个方案。

javascript 复制代码
const formatter = new Intl.NumberFormat('zh-CN', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
})

console.log(formatter.format(1234567.8))
// 1,234,567.80

如果想在金额前面加一个"钱"的符号,比如人民币就加个 ¥,可以这么写:

javascript 复制代码
const formatter = new Intl.NumberFormat('zh-CN', {
  style: 'currency',
  currency: 'CNY'
})

console.log(formatter.format(123456))
// ¥123,456.00

如果要使用美元符 $ 就这么写:

javascript 复制代码
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
})

console.log(formatter.format(123456))
// $123,456.00

方案3:正则实现千分位(不推荐)

javascript 复制代码
function formatNumber(num) {
  return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}

console.log(formatNumber(123456789))
// 123,456,789

如果需要支持小数就这么写:

javascript 复制代码
function formatMoney(num) {
  return num.toString().replace(/\d+/, function(n) {
    return n.replace(/(\d)(?=(\d{3})+$)/g, '$1,')
  })
}

但金融系统一般不推荐这种方式,因为这种方式不支持国际化,不支持货币格式,容易出 bug。

数字转大写中文

在中文的金融系统中,经常需要展示:

复制代码
123456.78
↓
壹拾贰万叁仟肆佰伍拾陆元柒角捌分

这个规则有点复杂,一般不建议自己实现。

我推荐使用开源库 nzh (github.com/cnwhy/nzh)。

安装:

bash 复制代码
npm install nzh

使用:

JavaScript 复制代码
import nzh from "nzh"

nzh.cn.encodeS(123456)
// 十二万三千四百五十六

nzh.cn.encodeB(123456.78)
// 壹拾贰万叁仟肆佰伍拾陆点柒捌

nzh.cn.toMoney(123456.78)
// 人民币壹拾贰万叁仟肆佰伍拾陆元柒角捌分

最后总结一下。

在 JavaScript 中处理金额和大数时,核心要记住三点:

1️⃣ 不要直接用 Number 做金融计算 2️⃣ 金额存储最好使用整数(分) 3️⃣ 展示时再进行格式化

只要遵循这三条原则,就可以避免绝大多数金额相关的 bug。

以上就是本文的全部内容了,还有疑问的话可以在评论区交流。

点赞 + 关注 + 收藏 = 学会了

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄4 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95274 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大4 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师4 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端