Java基本数据类型float/double因IEEE 754二进制存储导致精度丢失,无法通过写法避免;业务需精确计算时应禁用它们,改用BigDecimal并以字符串初始化,配合DECIMAL数据库字段及显式标度控制。

Java 基本数据类型本身无法避免精度丢失——这不是写法问题,而是设计本质。float 和 double 按 IEEE 754 标准用二进制存储十进制小数,像 0.1、0.2、0.3 这类数在二进制中是无限循环小数,必须截断,误差从赋值那一刻就已产生。所以,“怎么用基本类型”不解决问题;真正可行的是绕开它们。
别把 float/double 当计算主体
只要业务要求结果严格等于数学预期(比如金额、税率、计费、配置比例),就必须禁用 float 和 double 进行运算:
-
float:32 位,有效数字仅约 6–7 位,连
1234567.1都可能存不准 -
double:64 位,有效数字约 15–16 位,看似够用,但
0.1 + 0.2仍输出0.30000000000000004 -
整数类型(byte/short/int/long)虽无小数精度问题,但加减乘易溢出;
long最大值约9.2×10¹⁸,超限即翻转且不报错
用 BigDecimal,但必须正确初始化
BigDecimal 不是“修复”浮点数,而是换了一套系统:用字符串建模十进制数,内部用整数运算模拟四则运算。关键不在“用了”,而在“怎么用”:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- ✅ 正确构造:
new BigDecimal("19.99")或BigDecimal.valueOf(19.99)(后者内部调用Double.toString(),安全) - ❌ 绝对禁止:
new BigDecimal(19.99)—— 此时19.99已是 double 的近似值,错误被原样固化 - 前端传参、配置读取、用户输入等原始来源,应保持字符串形态,直通 BigDecimal 构造
数据库与序列化环节同步隔离 double 污染
精度污染常发生在上下游交接处:
立即学习“Java免费学习笔记(深入)”;
- 数据库字段必须定义为
DECIMAL(18,2)等定点类型,而非FLOAT/DOUBLE - JDBC 查询时用
resultSet.getBigDecimal("amount"),绝不调用getDouble()再转 - 写入时用
preparedStatement.setBigDecimal(1, amount),跳过 double 中间层 - JSON 序列化(如 FastJSON)需启用
WriteBigDecimalAsPlain,否则"1.00"可能变成"1"
运算过程显式控制标度和舍入
加减法天然保精度(标度自动对齐),但乘除必须人工干预:
-
add()/subtract():可直接调用,结果标度取操作数中较大者 -
multiply():建议用双参数版本,例如a.multiply(b, new MathContext(10, RoundingMode.HALF_UP)) -
divide():必须指定标度和舍入模式,如a.divide(b, 2, RoundingMode.HALF_EVEN)(金融常用银行家舍入) - 不指定时,
divide()可能抛ArithmeticException,multiply()可能产生远超业务需要的小数位

















