为何BigDecimal.multiply带MathContext重载方法比无参重载更慢?
问题描述
我正在执行基于任意小数精度的递归运算,但随着递归次数增加,所需精度会翻倍,因此必须在某个层级截断精度。我发现BigDecimal.multiply(BigDecimal, MathContext)是合适的方法,但它的执行速度明显慢于BigDecimal.multiply(BigDecimal)。查看内部代码后,我发现BigDecimal.divideAndRound()是性能不佳的主要原因,它的效率真的这么低吗?
测试性能的示例代码:
public static void main(String[] args) { BigDecimal b = new BigDecimal("0.4320843204893290423"); b = b.pow(20); int times = 1000000; MathContext context = new MathContext(100, RoundingMode.FLOOR); BigDecimal[] arr = new BigDecimal[times]; BigDecimal a; for (int i = 0; i < times; i++) { // 59ms a = b.multiply(b); arr[i] = a; } System.out.println(Arrays.stream(arr).mapToDouble(BigDecimal::doubleValue).sum()); for (int i = 0; i < times; i++) { // 2542ms a = b.multiply(b, context); arr[i] = a; } System.out.println(Arrays.stream(arr).mapToDouble(BigDecimal::doubleValue).sum()); }
解答
是的,divideAndRound()确实会带来显著的性能开销,核心原因在于两个multiply方法的逻辑差异:
- 无参
multiply:仅做纯数值运算——计算两个BigDecimal的未缩放值相乘,再调整结果的缩放比例,全程没有精度截断或舍入操作,所以速度极快。 - 带
MathContext的multiply:先完成完整乘法,再基于MathContext的规则处理精度:- 计算结果的有效数字位数,判断是否需要截断;
- 通过
divideAndRound()完成精度调整,这个方法要处理缩放比例重算、有效数字截断、舍入规则应用等复杂逻辑,每一步都有额外的数值判断和计算,自然会大幅拖慢执行速度。
针对你的递归运算场景,可以尝试这些优化手段:
- 减少截断频率:不要在递归每一层都执行精度截断,积累几次运算后再统一处理,降低
divideAndRound()的调用次数。 - 延后截断操作:先完成所有递归运算得到完整精度结果,再一次性应用目标精度截断,避免中间步骤的重复开销。
- 自定义轻量截断:如果业务允许,手动计算有效数字位置,直接修改
BigDecimal的未缩放值和缩放比例,跳过divideAndRound()中的冗余判断,实现更高效的精度控制。
内容的提问来源于stack exchange,提问作者noen
相关产品推荐
相关产品推荐

