如果您绝对不能使用BigDecimal
并且不想使用double
s,请使用long
s 进行定点算术(例如,每个long
值将代表美分的数量)。这将让您代表 18 位有效数字。
我会说使用joda-money,但这BigDecimal
在幕后使用。
编辑(因为上面并没有真正回答问题):
免责声明: 如果准确性对您很重要,请不要double
用来代表金钱。但似乎张贴者不需要精确的准确性(这似乎是关于可能具有超过 10**-12 内置不确定性的财务定价模型),并且更关心性能。假设是这种情况,使用 adouble
是可以原谅的。
一般来说,adouble
不能准确地表示小数。那么,a 有多不精确double
?对此没有简短的回答。
Adouble
可能能够很好地表示一个数字,您可以将该数字读入 a double
,然后再次将其写回,保留 15 位小数的精度。但由于它是二进制而不是小数,它不可能是精确的——它是我们希望表示的值,加上或减去一些错误。当执行许多涉及不精确的算术运算时double
,此错误的数量会随着时间的推移而增加,因此最终产品的精度小于十五位小数。少了多少?那要看。
考虑以下函数,它取n
1000 的 th 根,然后将其乘以自身n
:
private static double errorDemo(int n) {
double r = Math.pow(1000.0, 1.0/n);
double result = 1.0;
for (int i = 0; i < n; i++) {
result *= r;
}
return 1000.0 - result;
}
结果如下:
errorDemo( 10) = -7.958078640513122E-13
errorDemo( 31) = 9.094947017729282E-13
errorDemo( 100) = 3.410605131648481E-13
errorDemo( 310) = -1.4210854715202004E-11
errorDemo( 1000) = -1.6370904631912708E-11
errorDemo( 3100) = 1.1107204045401886E-10
errorDemo( 10000) = -1.2255441106390208E-10
errorDemo( 31000) = 1.3799308362649754E-9
errorDemo( 100000) = 4.00075350626139E-9
errorDemo( 310000) = -3.100740286754444E-8
errorDemo(1000000) = -9.706695891509298E-9
请注意,累积误差的大小不会与中间步骤的数量完全成比例地增加(实际上,它不是单调增加的)。给定一系列已知的中间操作,我们可以确定不准确的概率分布;虽然这将有更广泛的操作有更多的操作,确切的数量将取决于输入到计算中的数字。不确定性本身就是不确定性!
根据您正在执行的计算类型,您可以通过在中间步骤后四舍五入到整数单位/整数美分来控制此错误。(考虑一个银行账户持有 100 美元的情况,年利率为 6%,每月复利,因此每月利息为 0.5%。在贷记第三个月的利息后,您希望余额为 101.50 美元还是 101.51 美元?double
)小数单位(即美分)的数量而不是整数单位的数量会使这更容易 - 但如果你这样做,你也可以long
像我上面建议的那样使用 s 。
再次声明:浮点错误的累积使得使用double
s 来表示金额可能相当混乱。作为一个多年来一直使用double
十进制表示任何东西的Java开发人员,我会使用十进制而不是浮点算术来进行任何涉及金钱的重要计算。