Java中float/double因二进制表示限制无法保证精度,0.1等十进制小数在二进制中为无限循环小数,只能截断存储近似值,导致计算误差累积;货币、金融、科学计算等场景必须用BigDecimal,并规范构造、除法、比较等操作。

Java 中基本数据类型(float、double)**无法保证计算精度准确无误**,这是由其底层二进制表示机制决定的硬性限制,不是代码写法问题,而是 IEEE 754 标准本身的固有缺陷。
为什么 float/double 天然不准
十进制小数如 0.1、0.2、0.06 在二进制中是无限循环小数(类似十进制中 1/3 = 0.333…),而 float(23 位尾数)和 double(52 位尾数)只能截断存储——相当于“四舍五入”后存一个近似值。后续所有运算都基于这个带误差的近似值进行,误差会累积放大。
例如:
-
0.1 + 0.2→ 输出0.30000000000000004 -
1.0 - 0.9→ 输出0.09999999999999998 -
4.015 * 100→ 输出401.49999999999994
什么场景必须放弃基本类型
以下业务逻辑中,绝对不能用 float/double 做算术运算:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
- 货币金额(订单总价、找零、分账、手续费)
- 税率、折扣率、利率等百分比计算
- 科学实验数据、计量仪器读数处理
- 金融复利、年化收益等累积型计算
- 需要严格相等判断的比较(如
a == b)
真正能保证精度的正确做法
使用 java.math.BigDecimal,但必须按规范使用:
-
构造时禁用 double 参数:❌
new BigDecimal(0.1)会把 double 的误差直接带入;✅ 改用new BigDecimal("0.1")或BigDecimal.valueOf(0.1)(后者内部转字符串,更安全) -
除法必须指定 scale 和 RoundingMode:❌
bigA.divide(bigB)可能抛ArithmeticException;✅ 如bigA.divide(bigB, 2, RoundingMode.HALF_UP)表示保留 2 位小数,四舍五入 -
所有运算返回新对象:BigDecimal 不可变,
add/multiply等方法不修改原值,需重新赋值 -
比较用 compareTo(),不用 equals():因为
new BigDecimal("2.0")和new BigDecimal("2.00")的equals()返回false(标度不同),但compareTo()返回0
整数类型也要注意溢出
byte、short、int、long 虽然精确,但存在范围上限。例如 int 最大为 2147483647,超限会“绕回”成负数(如 Integer.MAX_VALUE + 1 == Integer.MIN_VALUE)。
- 超
long范围(约 ±9.2×10¹⁸)的大整数,改用BigInteger - 数据库主键 ID(如雪花算法生成的 19 位 long)传给前端时,JS 会因
Number安全整数限制(2⁵³−1)丢失精度 → 后端应统一转为String传输

















