You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何最小有符号64位整数的BigDecimal转Double再转回会失准?

问题

预期可精确表示为Double的BigDecimal值能在两者间干净往返(即BigDecimal转Double再转回BigDecimal后与原值相等),但在openjdk 11.0.19 2023-04-18 LTS中,以下测试用例失败,报错信息为org.opentest4j.AssertionFailedError: expected: <0> but was: <192>。

测试代码:

@Test
void checkThatMin64BitIntegerAsBigDecimalRoundTripsThroughDouble() {
  final var min64BitIntegerAsBigDecimal = new BigDecimal(
    "-9223372036854775808"
  );
  final var min64BitIntegerAsDouble = min64BitIntegerAsBigDecimal.doubleValue();
  final var min64BitIntegerAsBigDecimalAgain = BigDecimal.valueOf(
    min64BitIntegerAsDouble
  );
  assertEquals(
    BigDecimal.ZERO,
    min64BitIntegerAsBigDecimal.subtract(min64BitIntegerAsBigDecimalAgain)
  );
}

-2^63(即-9223372036854775808)是-1乘以2的幂,指数足够小,可精确表示为double,为何无法实现干净往返?

原因分析

问题出在BigDecimal.valueOf(double)的实现逻辑上:

  • BigDecimal.doubleValue()确实能把原BigDecimal正确转换为代表-2^63的double值,这个值本身是可以被double精确存储的。
  • 但BigDecimal.valueOf(double)在转换回BigDecimal时,会先调用Double.toString(double)把double值转为字符串,再用该字符串构造BigDecimal。而对于-2^63这个double值,Double.toString()返回的是"-9223372036854775616"——这是因为该方法会优先选择更短的十进制字符串来表示double值,而这个字符串对应的数值和原BigDecimal相差192,最终导致往返后的结果不一致。
解决办法

要实现这类整数型BigDecimal与double的精确往返,需要绕过依赖字符串转换的路径,改用基于整数类型的转换方式:

修改后的测试代码示例:

@Test
void checkThatMin64BitIntegerAsBigDecimalRoundTripsThroughDouble() {
  final var min64BitIntegerAsBigDecimal = new BigDecimal("-9223372036854775808");
  // 先转为精确的BigInteger,再转double,避免字符串转换的精度丢失
  final var min64BitIntegerAsDouble = min64BitIntegerAsBigDecimal.toBigIntegerExact().doubleValue();
  // 从double转回long,再构造BigDecimal
  final var min64BitIntegerAsBigDecimalAgain = new BigDecimal(BigInteger.valueOf((long) min64BitIntegerAsDouble));
  assertEquals(BigDecimal.ZERO, min64BitIntegerAsBigDecimal.subtract(min64BitIntegerAsBigDecimalAgain));
}

另一种思路:如果只是需要判断两者是否相等,也可以直接使用BigDecimal.compareTo()方法,而非通过减法判断是否为零,但这只是规避了表象问题,上述基于整数的转换才是解决往返精度问题的根本方案。

内容的提问来源于stack exchange,提问作者Eponymous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 02:43:18