javascript数组去重本身不引发浮点数精度问题,但因number类型固有缺陷(ieee 754),超大整数(如9999999999999999)或浮点数(如0.1+0.2)在存储时已失真,导致基于===的去重失效;有效方案包括:字符串归一化(tofixed)、bigint处理大整数、decimal.js高精度比对、后端返回字符串源头规避。

JavaScript 数组去重本身不直接引发浮点数精度问题,但当你处理包含超大数字或高精度浮点数的数组(例如 9999999999999999、0.1 + 0.2 结果、金融金额等)并试图去重时,精度误差会干扰“相等判断”,导致本该去重的值被当作不同项保留,或不该去重的被错误合并。
关键不是“去重方法错了”,而是原始数据在 JS 中已失真,后续所有基于 === 或 == 的比较都不可靠。
一、先确认:是不是真的遇到了精度导致的去重失效?
常见表现:
-
9999999999999999 === 10000000000000001返回true -
[0.1 + 0.2, 0.3].filter((x,i,a) => a.indexOf(x) === i)得到[0.30000000000000004, 0.3](两个“0.3”没去重) - 使用
new Set([0.1 + 0.2, 0.3])后长度为 2,而非 1
这说明:数值在存储阶段就已不精确,去重逻辑再严谨也无济于事。
二、真正有效的应对策略
✅ 方案1:统一转为字符串再比较(适用于已知精度要求)
比如金额统一保留2位小数:
function uniqueByFixed(arr, digits = 2) {
const seen = new Set();
return arr.filter(item => {
const key = Number(item).toFixed(digits);
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
uniqueByFixed([0.1 + 0.2, 0.3, 100.005, 100.0049]);
// → [0.3, 100.01] (按两位小数归一化后去重)
⚠️ 注意:
toFixed()返回字符串,确保比较的是格式化后的确定值,避开原始浮点误差。
Alibabacloud Sdk Client Initialization For Java下载在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
✅ 方案2:用 BigInt 处理超大整数(仅限整数场景)
当数组全是大整数(如订单ID、时间戳纳秒级),且超过 2^53 - 1(即 9007199254740991):
function uniqueBigInt(arr) {
const set = new Set();
return arr.map(n => BigInt(n))
.filter(bn => {
const str = bn.toString();
if (set.has(str)) return false;
set.add(str);
return true;
});
}
// 示例:[9999999999999999, 10000000000000001] → 原始是相等的,但 BigInt 可区分
?
BigInt能精确表示任意长度整数,但不能和number混用(1n === 1是false),务必统一类型。
✅ 方案3:使用第三方库做高精度数值比对(如 decimal.js)
适合金融、科学计算等强精度场景:
npm install decimal.js
const Decimal = require('decimal.js');
function uniqueByDecimal(arr, precision = 10) {
const seen = new Set();
return arr.filter(item => {
const d = new Decimal(item).toPrecision(precision);
if (seen.has(d)) return false;
seen.add(d);
return true;
});
}
uniqueByDecimal([0.1 + 0.2, 0.3, 1.005 * 100]);
// → [0.3, 100.5] (避免 1.005 * 100 → 100.49999999999999)
✅
Decimal内部用字符串+整数模拟十进制运算,彻底绕过 IEEE 754 缺陷。
✅ 方案4:业务层预处理——从源头规避
最稳妥的方式,不是在 JS 里“修数据”,而是:
- 后端返回金额、ID 等关键字段时,统一用字符串传输(如
"100.50"、"99999999999999999999") - 前端接收后,只做字符串比对或
BigInt解析,不经过Number()转换 - 对于浮点需求,约定固定小数位数,并全程用
toFixed(n)或Intl.NumberFormat格式化后再参与逻辑
例如:
// 接口返回 { price: "100.50", id: "12345678901234567890" }
const items = apiRes.data.map(item => ({
...item,
price: item.price, // 保持字符串
id: BigInt(item.id)
}));
const uniqueById = Array.from(new Set(items.map(i => i.id.toString())));
三、哪些方法要特别避开?
- ❌ 直接用
new Set(arr)处理未规整的浮点数组 → 失效 - ❌
arr.filter((x,i,a) => a.indexOf(x) === i)→indexOf依赖===,同样失效 - ❌
JSON.stringify序列化浮点数 → 会暴露0.30000000000000004这种字符串,导致误判 - ❌
Math.round(x * 100) / 100做简单四舍五入 → 无法解决9999999999999999类大数问题
不复杂但容易忽略:去重前,先问一句——这个数,JS 能精确存住吗?
能,就用 Set;不能,就别让它变成 number。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











