当前位置:  开发笔记 > 编程语言 > 正文

如何处理Java BigDecimal性能?

如何解决《如何处理JavaBigDecimal性能?》经验,为你挑选了6个好方法。

我为生活编写货币交易应用程序,所以我必须处理货币价值(遗憾的是,Java仍然没有十进制浮动类型,并且没有任何东西可以支持任意精确的货币计算)."使用BigDecimal!" - 你可能会说.我做.但现在我有一些代码在性能一个问题,BigDecimal为超过1000次(!)慢于double原语.

计算很简单:系统做什么是计算a = (1/b) * c很多很多次(其中a,bc有固定的点值).然而,问题在于此(1/b).我不能使用定点算术,因为没有固定点.而且BigDecimal result = a.multiply(BigDecimal.ONE.divide(b).multiply(c)不仅丑陋,而且缓慢.

我可以用什么来代替BigDecimal?我需要至少10倍的性能提升.我发现了其他优秀的JScience库,它具有任意精度的算术,但它甚至比BigDecimal慢.

有什么建议?



1> Vladimir Dyu..:

也许你应该开始用a = c/b替换a =(1/b)*c?这不是10倍,但仍然是一些东西.

如果我是你,我会创建自己的课程Money,这将保留很长的美元和长期美分,并在其中进行数学运算.


如果您以货币交易应用程序为生,那么这些计算就是您的"核心功能".您需要花费时间和精力才能使他们获得竞争优势.
对于通用库来说,这是一项艰巨的任务.对于特定应用(仅使用**子集**)的操作,它是微不足道的.事实上,我在自己的应用程序中有类,它只需要5或6个常用操作.
是的,我相信这就是他的建议.

2> Peter Lawrey..:

大多数双重操作可以提供足够的精度.你可以代表10万亿美元的精确度和双倍,这可能对你来说已经足够了.

在我所有的交易系统(四个不同的银行)中,他们使用了双倍的适当舍入.我没有看到任何使用BigDecimal的理由.



3> durron597..:

所以我原来的答案是错误的,因为我的基准写得很糟糕.我想我应该受到批评,而不是OP;)这可能是我写过的第一个基准之一......哦,这就是你学习的方式.而不是删除答案,这里是我没有测量错误的结果.一些说明:

预先计算数组,因此我不会通过生成它们来混淆结果

不要永远BigDecimal.doubleValue(),因为它是极其缓慢

不要通过添加BigDecimals来搞乱结果.只返回一个值,并使用if语句来阻止编译器优化.但是,确保在大多数情况下让它工作以允许分支预测消除代码的这一部分.

测试:

BigDecimal:完全按照你的建议做数学运算

BigDecNoRecip:(1/b)*c = c/b,只做c/b

双倍:用双打做数学

这是输出:

 0% Scenario{vm=java, trial=0, benchmark=Double} 0.34 ns; ?=0.00 ns @ 3 trials
33% Scenario{vm=java, trial=0, benchmark=BigDecimal} 356.03 ns; ?=11.51 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=BigDecNoRecip} 301.91 ns; ?=14.86 ns @ 10 trials

    benchmark      ns linear runtime
       Double   0.335 =
   BigDecimal 356.031 ==============================
BigDecNoRecip 301.909 =========================

vm: java
trial: 0

这是代码:

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Random;

import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;

public class BigDecimalTest {
  public static class Benchmark1 extends SimpleBenchmark {
    private static int ARRAY_SIZE = 131072;

    private Random r;

    private BigDecimal[][] bigValues = new BigDecimal[3][];
    private double[][] doubleValues = new double[3][];

    @Override
    protected void setUp() throws Exception {
      super.setUp();
      r = new Random();

      for(int i = 0; i < 3; i++) {
        bigValues[i] = new BigDecimal[ARRAY_SIZE];
        doubleValues[i] = new double[ARRAY_SIZE];

        for(int j = 0; j < ARRAY_SIZE; j++) {
          doubleValues[i][j] = r.nextDouble() * 1000000;
          bigValues[i][j] = BigDecimal.valueOf(doubleValues[i][j]); 
        }
      }
    }

    public double timeDouble(int reps) {
      double returnValue = 0;
      for (int i = 0; i < reps; i++) {
        double a = doubleValues[0][reps & 131071];
        double b = doubleValues[1][reps & 131071];
        double c = doubleValues[2][reps & 131071];
        double division = a * (1/b) * c; 
        if((i & 255) == 0) returnValue = division;
      }
      return returnValue;
    }

    public BigDecimal timeBigDecimal(int reps) {
      BigDecimal returnValue = BigDecimal.ZERO;
      for (int i = 0; i < reps; i++) {
        BigDecimal a = bigValues[0][reps & 131071];
        BigDecimal b = bigValues[1][reps & 131071];
        BigDecimal c = bigValues[2][reps & 131071];
        BigDecimal division = a.multiply(BigDecimal.ONE.divide(b, MathContext.DECIMAL64).multiply(c));
        if((i & 255) == 0) returnValue = division;
      }
      return returnValue;
    }

    public BigDecimal timeBigDecNoRecip(int reps) {
      BigDecimal returnValue = BigDecimal.ZERO;
      for (int i = 0; i < reps; i++) {
        BigDecimal a = bigValues[0][reps & 131071];
        BigDecimal b = bigValues[1][reps & 131071];
        BigDecimal c = bigValues[2][reps & 131071];
        BigDecimal division = a.multiply(c.divide(b, MathContext.DECIMAL64));
        if((i & 255) == 0) returnValue = division;
      }
      return returnValue;
    }
  }

  public static void main(String... args) {
    Runner.main(Benchmark1.class, new String[0]);
  }
}



4> DJClayworth..:

假设您可以使用一些任意但已知的精度(比如十亿分之一)并且具有已知的最大值,您需要处理(一万亿亿美元?),您可以编写一个类,将该值存储为十亿分之一的整数一分钱.你需要两个长的代表它.这应该是使用double的十倍慢; 大约是BigDecimal的一百倍.

大多数操作只是对每个部件执行操作并重新规范化.分部稍微复杂一些,但并不多.

编辑:回应评论.你需要在你的类上实现一个bithift操作(很容易,因为高long的乘数是2的幂).分裂移除除数直到它不比分红大; 将被移除的除数从被除数中减去并递增结果(适当的移位).重复.

再次编辑:你可能会发现BigInteger在这里做了你需要的.



5> Pesto..:

将多头存储为分数.例如,BigDecimal money = new BigDecimal ("4.20")成为long money = 420.你只需要记住修改100来获得输出的美元和美分.如果你需要追踪十分之一美分,它就会变成现在long money = 4200.


我需要跟踪(在中间计算中)十亿分之一美分.假设我们有美元/日元报价:99.223.在其他地方我需要一个JPY/USD报价,大约是0.0100779022(我需要更精确).

6> sfossen..:

您可能想要转到定点数学.现在只是搜索一些图书馆.在sourceforge 定点上我还没有深入研究过这个问题.beartonics

你用org.jscience.economics.money测试过吗?因为这确保了准确性.固定点只能与分配给每个部分的位数一样准确,但速度很快.

推荐阅读
放ch养奶牛
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有