总览
许多开发人员已确定BigDecimal是处理金钱的唯一方法。 通常,他们通过用BigDecimal替换double来确定错误或十个错误。 我对此没有说服力的是,也许他们可以解决double处理中的错误以及使用BigDecimal的额外开销。
根据我的比较,当被问到改善财务应用程序的性能时,我知道有时会删除BigDecimal(如果有的话)。 (通常不是造成延迟的最大原因,但是当我们修复系统时,它会升级为最严重的违规者)。
BigDecimal不能改善
BigDecimal有很多问题,因此请选择,但丑陋的语法也许是最糟糕的罪过。
- BigDecimal语法是不自然的。
- BigDecimal使用更多内存
- BigDecimal创建垃圾
- 对于大多数操作,BigDecimal的速度要慢得多(有例外)
以下JMH基准测试演示了BigDecimal的两个问题:清晰度和性能。
核心代码取两个值的平均值。
双重实现看起来像这样。 注意:需要使用四舍五入。
mp[i] = round6((ap[i] + bp[i]) / 2);
使用BigDecimal进行的相同操作不仅很长,而且还有很多样板代码需要导航
mp2[i] = ap2[i].add(bp2[i]).divide(BigDecimal.valueOf(2), 6, BigDecimal.ROUND_HALF_UP);
这会给您带来不同的结果吗? double的精度为15位,而数字远少于15位。 如果这些价格有17位数字,这将起作用,但对必须理解价格的穷人也不会起作用(即,它们永远不会变得难以置信的长)。
性能
如果您必须承担编码开销,通常这样做是出于性能方面的考虑,但这在这里没有意义。
基准测试 | 模式 | 样品 | 得分了 | 得分错误 | 单位 |
osMyBenchmark.bigDecimalMidPrice | thrpt | 20 | 23638.568 | 590.094 | 运算/秒 |
osMyBenchmark.doubleMidPrice | thrpt | 20 | 123208.083 | 2109.738 | 运算/秒 |
结论
如果您不知道如何使用双精度整数,或者您的项目要求使用BigDecimal,请使用BigDecimal。 但是,如果您有选择的话,不要仅仅认为BigDecimal是正确的选择。
编码
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;import java.math.BigDecimal;
import java.util.Random;@State(Scope.Thread)
public class MyBenchmark {static final int SIZE = 1024;final double[] ap = new double[SIZE];final double[] bp = new double[SIZE];final double[] mp = new double[SIZE];final BigDecimal[] ap2 = new BigDecimal[SIZE];final BigDecimal[] bp2 = new BigDecimal[SIZE];final BigDecimal[] mp2 = new BigDecimal[SIZE];public MyBenchmark() {Random rand = new Random(1);for (int i = 0; i < SIZE; i++) {int x = rand.nextInt(200000), y = rand.nextInt(10000);ap2[i] = BigDecimal.valueOf(ap[i] = x / 1e5);bp2[i] = BigDecimal.valueOf(bp[i] = (x + y) / 1e5);}doubleMidPrice();bigDecimalMidPrice();for (int i = 0; i < SIZE; i++) {if (mp[i] != mp2[i].doubleValue())throw new AssertionError(mp[i] + " " + mp2[i]);}}@Benchmarkpublic void doubleMidPrice() {for (int i = 0; i < SIZE; i++)mp[i] = round6((ap[i] + bp[i]) / 2);}static double round6(double x) {final double factor = 1e6;return (long) (x * factor + 0.5) / factor;}@Benchmarkpublic void bigDecimalMidPrice() {for (int i = 0; i < SIZE; i++)mp2[i] = ap2[i].add(bp2[i]).divide(BigDecimal.valueOf(2), 6, BigDecimal.ROUND_HALF_UP);}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(".*" + MyBenchmark.class.getSimpleName() + ".*").forks(1).build();new Runner(opt).run();}
}
翻译自: https://www.javacodegeeks.com/2014/07/if-bigdecimal-is-the-answer-it-must-have-been-a-strange-question.html