程序员数学 10:浮点数、误差与单位
数学中的实数模型与计算机里的有限位数表示不同。程序计算出现差异时,先判断是模型、表示、算法还是输入单位出了问题,不要一律归因于“精度问题”。
为什么 0.1 加 0.2 不精确等于 0.3
十进制的
js
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false这不是 JavaScript 独有的现象。可参照 Python 官方浮点说明 理解同样的二进制表示机制。格式化成两位小数改变显示,不会自动修正之前的计算误差。
比较误差时同时考虑尺度与零点
常用的近似相等判据是:
绝对容差处理接近零的情况,相对容差处理不同数值尺度。容差应来自测量精度或业务允许误差,而不是随意复制一个 1e-9。
js
// 示例只比较有限数值;容差由调用者按单位和任务选择。
function nearlyEqual(a, b, absTol, relTol) {
if (![a, b, absTol, relTol].every(Number.isFinite)) return false;
if (absTol < 0 || relTol < 0) return false;
return Math.abs(a - b) <= Math.max(absTol, relTol * Math.max(Math.abs(a), Math.abs(b)));
}
console.log(nearlyEqual(0.1 + 0.2, 0.3, 1e-12, 1e-12)); // true
console.log(nearlyEqual(NaN, 0, 1e-12, 1e-12)); // false这是常规有限数值范围的教学实现,极端量级仍应检查中间计算是否溢出。近似相等通常不满足传递性,不能直接作为哈希键的相等关系。ID、计数和版本号也不应使用浮点容差来“差不多比较”。
整数同样有表示边界
JavaScript Number 能精确区分的安全整数范围是
需要更大整数时应从输入阶段选择字符串或 BigInt 等精确表示。先把超大十进制整数读成 Number 再转为 BigInt,已经丢失的位不会恢复。
对必须精确到固定最小单位的金额或计量,常用整数最小单位或十进制定点/任意精度库。要规定舍入时机与规则,并检查范围;把不精确的浮点金额乘 100 并不自动获得精确整数。
单位错误比小数误差更大
如果期限是 2 秒,当前耗时是 1500 毫秒,直接比较 1500 > 2 会得出错误判断。应统一成同一单位,再比较 1500 > 2000。
js
const timeoutSeconds = 2;
const elapsedMs = 1500;
console.log(elapsedMs > timeoutSeconds * 1000); // false变量名写出单位、函数边界声明单位,往往比增加小数位更有效。吞吐量“个/秒”、延迟“秒”和并发数“个”也不能互换。
练习
- 相对容差比较中,为什么常需要额外的绝对容差?
- 先以浮点读取一个超大 ID,再转为字符串,能保证恢复原始 ID 吗?
- 500 ms 与 0.6 s 哪个更大?
查看答案与讲解
- 接近零时,相对阈值也接近零,可能无法表达允许的固定微小误差。绝对容差给出相应单位下的下限。
- 不能。读入时可能已经舍入,后续转换只是保留舍入后的值。应在读入前选择精确表示。
- 0.6 s 等于 600 ms,因此更大。先统一单位,不能直接比较 500 与 0.6。
继续阅读
- 习题与解析
- 习题与解析
- 习题与解析