Jest测试遇数字比较异常:价格计算结果出现精度偏差
JavaScript浮点数精度导致价格计算偏差问题解决办法
问题本质
这不是代码逻辑错误,而是JavaScript二进制浮点数的精度缺陷导致的。JavaScript采用IEEE 754双精度浮点数标准,只能精确表示分母为2的幂的十进制小数(比如0.5、0.25),像0.4这类小数无法被二进制精确存储,多次乘法、加法运算后,微小的误差会累积,最终出现184.39999999999998这类和预期184.4不一致的结果。
解决方案
方案1:改用整数(分)运算(最彻底)
将货币单位从欧元转换为分,全程用整数进行计算,避免浮点数运算。最后再将结果转换回欧元,从根源上消除精度问题。
代码修改示例:
// 常量改用分作为单位,避免浮点数 const PRICE_CENTS = 800; // 对应原价8€ // 折扣改用百分比整数(100表示无折扣,95表示95折) const DISCOUNT = [0, 100, 95, 90, 80, 75]; export class AppService { calculatePrice(books: Book[]): number { return this.calculatePriceCents(books) / 100; } // 内部用分计算的递归函数 private calculatePriceCents(books: Book[]): number { if (books.length === 0) return 0; if (books.length === 1) return PRICE_CENTS; const flatBooks = books.flat(2); const setOfBooks = [...new Set(flatBooks)]; // 整数运算,无精度损失 const cumulateCents = PRICE_CENTS * setOfBooks.length * DISCOUNT[setOfBooks.length] / 100; for (const book of setOfBooks) { const index = flatBooks.indexOf(book); flatBooks.splice(index, 1); } return cumulateCents + this.calculatePriceCents(flatBooks); } }
方案2:结果四舍五入(快速修复)
如果不想大规模修改代码,可以在最终返回结果时,对数值进行四舍五入并保留两位小数。注意toFixed()返回的是字符串,需要转换为数字类型。
代码修改示例:
export class AppService { calculatePrice(books: Book[]): number { if (books.length === 0) return 0; if (books.length === 1) return PRICE; const flatBooks = books.flat(2); const setOfBooks = [...new Set(flatBooks)]; const cumulate = PRICE * setOfBooks.length * DISCOUNT[setOfBooks.length]; for (const book of setOfBooks) { const index = flatBooks.indexOf(book); flatBooks.splice(index, 1); } const total = cumulate + this.calculatePrice(flatBooks); // 四舍五入到两位小数 return Number(total.toFixed(2)); } }
⚠️ 注意:此方案适合最终展示场景,如果后续还需要对结果进行运算,可能再次引入精度问题,因此方案1更可靠。
方案3:使用精确计算库
如果项目允许添加依赖,可以使用decimal.js、big.js等专门处理高精度数值计算的库,这类库会规避IEEE 754的精度限制。
内容的提问来源于stack exchange,提问作者Wajdi Ben Abdallah
相关产品推荐
相关产品推荐

