我编写货币交易应用程序是为了谋生,所以我必须处理货币值(遗憾的是,Java仍然没有decimal float类型,也没有支持任意精度的货币计算)。“使用BigDecimal!”--你可能会说。我知道。但现在我有一些代码,其中性能是的一个问题,而BigDecimal超过1000倍(!)比double
原语慢。
计算非常简单:系统所做的是多次计算a = (1/b) * c
(其中a
、b
和c
是定点数值)。然而,问题出在这个(1/b)
上。我不能用定点算术,因为没有不动点。而且BigDecimal result = a.multiply(BigDecimal.ONE.divide(b).multiply(c)
不仅丑陋,而且运行缓慢。
我可以用什么来替代BigDecimal?我需要至少10倍的性能提升。我发现其他方面都很好的JScience library,它有任意精度的算术,但它甚至比BigDecimal还慢。
有什么建议吗?
发布于 2009-03-04 18:02:07
也许您应该从a= (1/b) *c替换为a= c/b开始?虽然不是10倍,但仍然很不错。
如果我是你,我会创造我自己的班级资金,它将保留长期美元和长期美分,并在其中进行数学计算。
发布于 2009-03-04 19:26:33
大多数double操作都会给你足够的精确度。你可以用两倍的精度代表10万亿美元,这对你来说可能已经足够了。
在我工作过的所有交易系统中(四家不同的银行),他们都使用了双精度,并进行了适当的舍入。我看不出有任何理由要使用BigDecimal。
发布于 2013-02-07 00:23:26
所以我最初的回答是完全错误的,因为我的基准测试写得很糟糕。我想我才是那个应该被批评的人,而不是OP ;)这可能是我写的第一个基准测试之一……哦,好吧,这就是你学习的方式。这里的结果不是删除答案,而是我没有测量错误的东西。一些注意事项:
BigDecimal.doubleValue()
,因为它非常慢BigDecimal
s来扰乱结果,只返回一个值,并使用if语句来阻止编译器优化。不过,请确保它在大部分时间内都能正常工作,以允许分支预测消除该部分代码。测试:
Double
执行数学运算
下面是输出:
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]);
}
}
https://stackoverflow.com/questions/611732
复制相似问题