
json 中的高精度数字(如 `"amount": 123345555789123495.38`)若被解析为 `double` 再转 `bigdecimal`,会因 `double` 二进制精度限制导致严重失真;正确做法是**跳过 `double` 中间表示,直接从原始 json 字符串构造 `bigdecimal`**。
在 Java 处理 JSON 数据时,若使用 Jackson 等库默认将数值字段反序列化为 Double(例如通过 ConcurrentHashMap<String, Object> 接收),一旦 123345555789123495.38 被解析为 double,其内部已丢失精度——double 仅能精确表示约 15–17 位十进制有效数字,而该数字整数部分达 17 位,小数部分又含两位,超出 double 表示能力,实际存储值变为 1.23345555789123488E17(即 123345555789123488.00),误差达 7 元。
✅ 正确方案:绕过 double,保留原始 JSON 字符串形式。推荐两种实践方式:
方式一:使用 Jackson 的 JsonNode 显式读取为字符串
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);
String amountStr = rootNode.path("amount").asText(); // 确保未被转为 number
BigDecimal amount = new BigDecimal(amountStr).setScale(2, RoundingMode.HALF_UP);方式二:自定义反序列化器(推荐用于实体类)
public class AmountDeserializer extends JsonDeserializer<BigDecimal> {
@Override
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
// 强制按字符串读取,避免 double 解析
String text = p.getText();
return new BigDecimal(text).setScale(2, RoundingMode.HALF_UP);
}
}
// 使用示例
public class Order {
@JsonDeserialize(using = AmountDeserializer.class)
private BigDecimal amount;
}⚠️ 注意事项:
- ❌ 避免 new BigDecimal(String.valueOf(doubleValue)):String.valueOf(1.23345555789123488E17) 仍基于已失真的 double,无法恢复原始精度;
- ✅ 始终优先从 JsonParser.getText() 或 JsonNode.asText() 获取原始字符串;
- ? 若业务要求严格金融精度,建议服务端统一以字符串格式传输金额字段(如 "amount": "123345555789123495.38"),并在 Schema 层面约束类型;
- ? setScale(2, RoundingMode.HALF_UP) 更符合金融四舍五入惯例(HALF_EVEN 虽标准但易引发业务理解偏差,需与财务规范对齐)。
总结:精度丢失根源不在 BigDecimal,而在 double 这一中间环节。只要确保 JSON 数字未经 double 解析、直接以字符串进入 BigDecimal 构造器,即可实现零精度损失的高保真转换。










