Java浮点数用二进制科学计数法逼近十进制小数,遵循IEEE 754标准,float(32位)精度约6–7位、double(64位)约15–16位;0.1因二进制无限循环而无法精确表示,导致0.1+0.2≠0.3;比较需用误差容限,高精度场景应使用字符串构造的BigDecimal。

Java浮点类型不是“小数的直接表示”,而是用二进制科学计数法逼近十进制小数。精度问题不是bug,而是IEEE 754标准在有限位宽下的必然结果;内存存储结构决定了它能精确表示哪些数、为何0.1 + 0.2 ≠ 0.3。
float与double的内存布局差异
两者都按IEEE 754规范拆分为三部分:符号位、指数位、尾数位(也称有效数字位),但位宽不同,导致精度与范围显著不同:
- float(32位):1位符号 + 8位指数(偏移量127)+ 23位尾数 → 实际有效精度约6–7位十进制数字,可表示范围约±10−38 到 ±1038
- double(64位):1位符号 + 11位指数(偏移量1023)+ 52位尾数 → 实际有效精度约15–16位十进制数字,可表示范围约±10−308 到 ±10308
关键细节:尾数位隐含一个前导1(归一化形式),所以float实际有24位精度、double有53位——但这仍是二进制精度,不等于十进制精度。
为什么0.1无法被精确存储?
因为0.1的十进制小数在二进制中是无限循环小数:
0.110 = 0.00011001100110011…2(循环节“0011”)
float或double只能截断保存有限位,于是存入的是近似值,如double存的是≈0.10000000000000000555…
这种误差在单次运算中微乎其微,但会累积:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
- 连续加0.1十次,结果≠1.0
- 金融计算中0.1元 × 10 ≠ 1.0元(严格意义上)
- 大整数转float后+1可能仍等于原值(如2³²以上整数在float中无法区分相邻值)
浮点比较与使用避坑要点
直接用==判断两个浮点数是否相等,99%会出错。正确做法是引入误差容限(epsilon):
- 用
Math.abs(a - b) 代替<code>a == b(double场景常用1e-10~1e-15) - float建议用
1e-6级别容限,因本身精度仅约1e-7 - 避免混合使用float/double参与同一计算链(如
float f = 0.1f; double d = f + 0.2;),隐式提升会放大不一致 - 非必要不使用float——现代JVM和硬件对double运算效率已接近float,且double精度更安全
高精度场景必须用BigDecimal
当业务要求“绝对精确”(如金额、税率、科学常量、审计数据),float/double必须让位:
- BigDecimal基于十进制字符串构造(
new BigDecimal("0.1")),避免二进制转换失真 - 所有算术操作需显式调用
add()、multiply()等方法,并指定舍入模式(如RoundingMode.HALF_UP) - 不要用
BigDecimal.valueOf(double)传原始小数——它会先走double的不精确路径;应始终用字符串构造 - 注意性能开销:BigDecimal是对象,运算比原生浮点慢得多,仅在必要时启用

















