沉默是金,总会发光
大家好,我是沉默
面试官问:“0.1+0.2 为什么不等于 0.3?”我笑了,但他又补一句:“那为什么 0.1+0.1 又等于 0.2?”
这题我当时也懵了。
背会“浮点数精度问题”没用,面试官根本不想听口诀。
他想知道:你,到底懂不懂计算机怎么存 0.1?
**-**01-
这题的坑:要懂底层逻辑
小林在面试时被问到:
“为什么 0.1+0.2 ≠ 0.3?”
“那为啥 0.1+0.1 又等于 0.2?”
他脱口而出:“因为 0.1 不能被二进制精确表示!”
面试官点点头,又追问一句:“那为什么两个不精确的数相加,反而等于精确的 0.2 呢?”
小林:“呃……这……可能是巧合吧……”
其实,这不是巧合,而是 IEEE 754 浮点数的“舍入抵消”机制在起作用。
- 02-
计算机是怎么存小数的?
在计算机眼里,所有浮点数都是科学计数法 + 二进制表示的近似值。
IEEE 754 双精度浮点数结构(Java 的 double)
| 部分 | 位数 | 含义 | 示例(以 0.1 为例) |
|---|---|---|---|
| 符号位 | 1 | 正负号,0 为正,1 为负 | 0 |
| 指数位 | 11 | 记录指数 + 偏移量(1023) | -4 → 存 1019 |
| 尾数位 | 52 | 存储有效二进制小数部分(舍入后) | 1001100…(循环) |
重点:
0.1 的二进制是个“无限循环小数”:
0.1₁₀ = 0.00011001100110011...(0011 无限循环)
计算机无法完整存下,只能取前 52 位,然后做舍入(rounding) 。
所以——计算机存的“0.1”,其实是个近似值,不是数学意义上的 0.1。
- 03-
案例拆解
案例1:0.1 + 0.1 = 0.2(误差抵消)
两个“近似 0.1”相加:
0.0001100110011... × 2^0+0.0001100110011... × 2^0=0.001100110011...₂
刚好等于 0.2 的二进制!
而且 0.2 的循环模式刚好能被 52 位“完整存下”,所以结果精确无误差。
验证
System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.1)));// 输出:0.200000000000000011102230246251565404236316680908203125System.out.println(new BigDecimal(0.2));// 输出:0.200000000000000011102230246251565404236316680908203125
两者完全一致 —— 计算机判定:相等
案例2:0.1 + 0.2 ≠ 0.3(误差叠加)
0.1 → 0.000110011...0.2 → 0.00110011...相加 → 0.0100110011...
看似是 0.3,但转换成 IEEE 754 存储后,循环部分被截断、舍入,误差没抵消,反而叠加。
验证
System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2)));// 0.3000000000000000444089209850062616169452667236328125System.out.println(new BigDecimal(0.3));// 0.299999999999999988897769753748434595763683319091796875
不一样!
这就是“0.1+0.2≠0.3”的根源:二进制循环 + 舍入误差叠加。
**-****04-**总结
面试模板回答:3步搞定
| 步骤 | 回答内容 | 举例或关键词 |
|---|---|---|
| Step 1 | 原理说明:浮点数采用 IEEE 754 存储,小数转二进制会出现无限循环,只能存近似值。 | “0.1 在二进制中无限循环,存储时被舍入” |
| Step 2 | 对比案例:两个近似值相加时,误差可能抵消(0.1+0.1)或叠加(0.1+0.2)。 | “所以一个等,一个不等” |
| Step 3 | 工程建议:避免浮点误差,用 BigDecimal 或整数化处理。 | “金额计算用分,不用 double” |
举例回答模板:
“这是 IEEE 754 浮点数精度问题。像 0.1 这样的数在二进制中是无限循环小数,只能被舍入成近似值。
0.1+0.1 时误差抵消,结果等于 0.2;但 0.1+0.2 时误差叠加,和 0.3 的存储值不一致。
实际开发中要用 BigDecimal 或整数计算来避免精度丢失。”
开发中怎么避坑?
金额计算
用 BigDecimal(String),别用 new BigDecimal(double)
BigDecimal a = new BigDecimal("0.1");BigDecimal b = new BigDecimal("0.2");System.out.println(a.add(b)); // 精确等于0.3
分制法(整数化)
金额统一乘以 100,用分来算:
int total = 10 + 20; // 单位分System.out.println(total / 100.0); // 输出0.3
打印调试
遇到异常浮点结果,用 BigDecimal 打印真实存储值。
会用的人,不怕“陷阱题”
浮点数这题,其实不是考死记硬背,而是考你能否:
- 从“现象”追到“原理”
- 从“误差”说到“工程实践”
面试官要的是一个“懂底层逻辑、又能写出可靠代码”的你。
你面试时遇到过哪些看似简单、实则坑人题?
留言聊聊,也许下一篇我们就一起拆!
**-****05-**粉丝福利
站在职业的十字路口,我们或许都曾感到迷茫:
投出的简历总是没有回音?
面试时不知如何展现自己的优势?
未来的职场道路该如何规划?
技术管理能力提升,如何跨越第一步?
如果你正在经历这些,我很乐意用我的经验为你提供一些帮助。
无论是修改简历、1对1求职陪跑,职业规划咨询,
还是迈向技术Leader或提升管理效能,
欢迎你加我,我们像朋友一样聊聊。