
json 中的高精度数字(如 "123345555789123495.38")若经 double 中间解析会丢失精度;正确做法是**跳过 double 类型,直接从原始字符串构造 bigdecimal**。
在 Java 处理 JSON 数据时,若后端将 JSON 数字字段(如 "amount": 123345555789123495.38)反序列化为 double 或 Double 类型,会导致严重精度丢失——因为 double 仅能精确表示约 15–17 位十进制有效数字,而示例中的整数部分已达 17 位(123345555789123495),超出 double 的安全整数范围(Long.MAX_VALUE ≈ 9.2×10¹⁸,但精度限制更早发生)。此时 123345555789123495.38 被转为 double 后实际存储为 1.23345555789123488E17,即 123345555789123488.0,已丢失末尾 7 个单位的整数精度。
根本解决方法是:避免经过 double 解析环节,直接获取 JSON 中该字段的原始字符串表示,再用 BigDecimal(String) 构造器初始化。BigDecimal 的字符串构造器可完全保留任意长度的十进制数字精度。
以 Jackson 为例,推荐以下实践方式:
✅ 正确做法(推荐):
// 假设使用 Jackson,且已通过 JsonNode 或 ObjectMapper 获取原始值
JsonNode node = objectMapper.readTree(jsonString);
String amountStr = node.path("amount").asText(); // 确保 asText() —— 不调用 asDouble()
BigDecimal amount = new BigDecimal(amountStr).setScale(2, RoundingMode.HALF_UP);✅ 若必须兼容现有 ConcurrentHashMap
Object rawValue = extensionMap.get("amount");
BigDecimal amount;
if (rawValue instanceof String) {
amount = new BigDecimal((String) rawValue).setScale(2, RoundingMode.HALF_UP);
} else if (rawValue instanceof Number) {
// ⚠️ 警告:此处 Number 可能已是 double/Long,精度已失!应尽量避免进入此分支
amount = BigDecimal.valueOf(((Number) rawValue).doubleValue()) // ❌ 不推荐!
.setScale(2, RoundingMode.HALF_UP);
} else {
throw new IllegalArgumentException("Unsupported amount type: " + rawValue.getClass());
}⚠️ 注意事项:
- 永远不要用 new BigDecimal(double):该构造器会继承 double 的二进制浮点误差,例如 new BigDecimal(123345555789123495.38) 实际传入的是近似值。
- 使用 BigDecimal(String) 是唯一可靠方式,它按字面量精确解析。
- 在 Jackson 配置中,可全局禁用 double 自动转换,强制数字字段走字符串路径(如注册自定义 JsonDeserializer
)。 - 若前端无法保证数字始终以字符串形式发送(如 {"amount": "123345555789123495.38"}),则需服务端统一约定:关键金额字段必须为 JSON 字符串类型,这是金融级系统常见规范。
总结:精度保障始于数据源头。只要 JSON 中的数值以字符串形式传递,并在 Java 层跳过任何 double 解析步骤,即可实现任意长度小数的无损 BigDecimal 转换。










