如何用浮点运算解决精度误差

How to resolve the accuracy error with floating point arithmetic

我正在尝试制作一个程序来计算给定金额的等量找零。在我精打细算之前,程序中的所有内容都运行良好。当我精打细算时,我做了太多的浮点运算,以至于数值变得不准确。这是输出:

正如我们所见,不是 0.2(我会除以 0.1 得到 2,正确答案),而是 019999999999959064(除以 0.01 得到便士的数量,我们得到 1 而不是2).我该如何解决这个问题,才能获得正确数量的美分?

你不能。浮点不是建模精确货币值的好选择。

要么 (i) 使用 long 类型并以便士为单位工作,要么 (ii) 使用 class 设计的货币模型。

Math.round 是解决此问题的一种方法,但我不推荐它。

另一种方法是使用 BigDeciaml,它更准确,尤其是在处理货币时

我最近读到的另一种方法是计算整数直到结束,例如以美分而不是美元计算,因此 1 美元将变成 100,然后从那里开始计算。

希望这些想法对您有所帮助

very intereesting video on floating points in general

math api

谩骂

停止使用浮点数来表示货币。 BigDecimal 同样糟糕,但精度(可能 none)问题较少。

如果您使用 BigDecimal, 我建议您在代码中包含以下注释:

// Danger, this code written by a goofball (insert your name here).

真正的解决方案

使用多头并表示从有意义的(对您的情况而言)一分钱的一小部分开始的值;也许是一分钱的千分之一。 即使你代表一分钱的百万分之一, 很长的时间仍然提供了足够的空间来显着超过 9,223,372,036.99 美元 这可能比您需要处理的任何交易都大。

您需要使用适当的舍入。即使您使用 longBigDecimal 舍入问题也不会消失,但在这种情况下会简单得多。

一个好的开始是四舍五入到 long 美分数。

double value = 586.67;
long v = Math.round(value * 100.0); // the fact you used a double is fine.
long hundreds = v / 100;
v -= hundreds * 100;
long fifties = v / 50;
v -= fifties * 50;
long twenties = v / 20;
v -= twenties * 20;
long tens = v / 10;
v -= tens * 10;

您可以使用 double 来执行此操作,但是您需要对每个步骤进行舍入,这在本例中更为复杂。