Closed. This question is opinion-based。它当前不接受答案。












想改善这个问题吗?更新问题,以便editing this post用事实和引用来回答。

在11个月前关闭。



Improve this question




我知道这个问题已经讨论过几次了,但是我对答案并不完全满意。请不要回答“双精度数不正确,您不能代表0.1!您必须使用BigDecimal” ...

基本上,我在做财务软件,我们需要在内存中存储很多价格。 BigDecimal太大,无法容纳在缓存中,因此我们决定切换为double。
到目前为止,由于充分的原因,我们还没有遇到任何错误,我们需要12位数字的准确性。 12位数字的估算是基于这样的事实,即使我们以百万为单位讲话,我们仍然能够处理美分。

双精度数提供15位有效的十进制数字精度。如果您在必须显示/比较双打时对其进行四舍五入,那会出什么问题?

我猜问题在于积累的不准确性,但是有多糟糕呢?在影响第12位数字之前需要进行多少次操作?

您是否发现双打还有其他问题?

编辑:大约很长一段时间,这绝对是我们考虑过的事情。我们正在做很多除法运算,但长期以来并不能很好地处理(丢失小数和溢出),或者至少您必须非常小心自己的操作。我的问题更多地是关于双打理论,基本上它有多糟糕,并且这种误差是否可以接受?

EDIT2:不要尝试解决我的软件,我的准确性不高:)。我重新措辞一个问题:如果只需要12位数字,并且在显示/比较时四舍五入为双精度数字,发生不准确的可能性有多大?

最佳答案

如果您绝对不能使用BigDecimal并且不想使用double,请使用long来执行fixed-point arithmetic(例如,每个long值将代表美分)。这将使您代表18个有效数字。

我会说使用joda-money,但这在幕后使用了BigDecimal

编辑(因为以上内容并未真正回答问题):

免责声明:如果准确性对您很重要,请don't use double to represent money。但是似乎发布者并不需要确切的准确性(这似乎是一种金融定价模型,该模型可能具有10 **-12以上的内置不确定性),并且更在意性能。假设是这种情况,可以使用double

通常,double不能精确表示十进制分数。那么,double是多么不精确?没有简短的答案。
double可能足以表示一个数字,您可以将其读入double中,然后再次写回,保留15位精度的十进制数字。但是因为它是二进制而不是小数,所以它不是精确的-它是我们希望表示的值,加上或减去一些错误。当执行许多涉及不精确double的算术运算时,随着时间的流逝,此错误的数量会逐渐增加,从而最终产品的准确度少于15个十进制数字。少几个?那要看。

考虑以下函数,该函数将n的根数取为1000,然后将其自身乘以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的使用可能会带来很多麻烦。作为一名Java开发人员,他使用double来表示多年来输入到他的任何东西的十进制表示法是邪恶的,对于涉及金钱的任何重要计算,我都会使用十进制而不是浮点运算。

07-24 09:45
查看更多