解决JavaScript中toFixed和Math.round的跨浏览器舍入错误问题
JavaScript中精确舍入35.855至两位小数的解决方案
问题根源
JavaScript采用二进制浮点数存储数值,35.855无法被精确表示,实际存储的是一个接近它的近似值。不同浏览器对这个近似值的计算细节存在差异:
- Chrome中存储的近似值略小于35.855,乘100后为
3585.4999999999995,Math.round后得到3585,最终结果35.85 - IE9中存储的近似值更接近35.855,乘100后为
3585.5,Math.round后得到3586,最终结果35.86
原生Math.round和toFixed依赖二进制浮点数计算,容易因精度误差导致舍入结果不一致,需要自定义精确舍入逻辑。
通用解决方案
方法1:使用BigInt实现精确舍入
利用BigInt避免浮点数运算的精度丢失,同时添加极小值修正浮点数近似误差:
function preciseRound(num, decimals) { const power = 10 ** decimals; // 加Number.EPSILON*power修正浮点数精度偏差 const scaled = num * power + Number.EPSILON * power; return Number((BigInt(Math.round(scaled)) / BigInt(power)).toString()); } // 测试用例 console.log(preciseRound(35.855, 2)); // 输出35.86 console.log(preciseRound(2.5, 0)); // 输出3 console.log(preciseRound(1.2345, 3)); // 输出1.235
方法2:字符串解析实现精确舍入
若环境不支持BigInt,可通过字符串解析手动处理进位逻辑,完全基于十进制运算:
function preciseRound(num, decimals) { const numStr = num.toString(); if (!numStr.includes('.')) return Number(numStr); const [integerPart, decimalPart] = numStr.split('.'); // 截取目标小数位+1位,用于判断是否进位 const targetDigits = decimalPart.slice(0, decimals + 1); let resultInt = integerPart; let resultDec = targetDigits.slice(0, decimals); const nextDigit = parseInt(targetDigits[decimals], 10) || 0; if (nextDigit >= 5) { let carry = 1; // 处理小数部分进位 const decArr = resultDec.split('').reverse(); for (let i = 0; i < decArr.length && carry; i++) { const newDigit = parseInt(decArr[i], 10) + carry; if (newDigit === 10) { decArr[i] = '0'; carry = 1; } else { decArr[i] = newDigit.toString(); carry = 0; } } resultDec = decArr.reverse().join(''); // 处理整数部分进位(若小数进位后仍有剩余进位) if (carry) { const intArr = resultInt.split('').reverse(); for (let i = 0; i < intArr.length && carry; i++) { const newDigit = parseInt(intArr[i], 10) + carry; if (newDigit === 10) { intArr[i] = '0'; carry = 1; } else { intArr[i] = newDigit.toString(); carry = 0; } } resultInt = carry ? '1' + intArr.reverse().join('') : intArr.reverse().join(''); } } // 去除小数末尾的无效0,若小数部分为空则仅返回整数 resultDec = resultDec.replace(/0+$/, ''); return resultDec ? Number(`${resultInt}.${resultDec}`) : Number(resultInt); } // 测试用例 console.log(preciseRound(35.855, 2)); // 输出35.86 console.log(preciseRound(1.005, 2)); // 输出1.01 console.log(preciseRound(99.995, 2)); // 输出100
内容的提问来源于stack exchange,提问作者Sercan Samet Savran
相关产品推荐
相关产品推荐

