简体   繁体   English

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

[英]How to resolve the accuracy error with floating point arithmetic

I am trying to make a program that calculates the equivalent amount of change for a given amount of money. 我正在尝试制定一个计算给定金额的等价变化量的程序。 Everything in the program works well until I get to pennies. 程序中的所有内容都运行良好,直到我得到便士。 When I get to pennies, I've done so much floating point arithmetic that the value becomes inaccurate. 当我得到便士时,我已经做了很多浮点算术,这个值变得不准确。 Here is the output: 这是输出:

在此输入图像描述

As we can see, rather than have 0.2 (which I would divide by 0.1 to get 2, the correct answer), we have 019999999999959064 (which when divided by 0.01 to get the amount of pennies, we get 1 not 2). 我们可以看到,而不是0.2(我将除以0.1得到2,正确答案),我们有019999999999959064(当除以0.01得到便士的数量时,我们得到1而不是2)。 How can I resolve this issue so that I can get the correct amount of pennies? 如何解决这个问题,以便获得正确数量的便士?

You can't. 你不能。 Floating point is not a good choice for modelling exact monetary values. 浮点不是建模精确货币值的好选择。

Either (i), use a long type and work in pence, or (ii) use a class designed to model money. (i),使用long型并使用便士,或(ii)使用旨​​在模拟金钱的类。

Diatribe 诽谤

Stop using floating point to represent currency. 停止使用浮点表示货币。 BigDecimal is just as bad, but will have fewer precision (perhaps none) issues. BigDecimal同样糟糕,但精度较低(可能没有)。

If you use BigDecimal, I suggest that you include the following comment in your code: 如果您使用BigDecimal,我建议您在代码中包含以下注释:

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

The Real Solution 真正的解决方案

Use longs and represent values starting at a meaningful (to your situation) fraction of a penny; 使用long并表示从一个有意义的(对你的情况)一小部分开始的值; perhaps 1000th of a penny. 也许是一分钱的1000分。 Even if you represent 1,000,000th of a penny, a long still provides plenty of room to significantly more than $9,223,372,036.99 which is likely to be larger than any transaction you need to process. 即使您代表1,000,000美元的一分钱,长期仍然提供足够的空间,远远超过$ 9,223,372,036.99,这可能比您需要处理的任何交易都要大。

You need to use appropriate rounding. 您需要使用适当的舍入。 Even if you use long or BigDecimal rounding issues don't go away, though in this case it would be much simpler. 即使你使用longBigDecimal舍入问题也不会消失,尽管在这种情况下它会更简单。

A good place to start is rounding to a long number of cents. 一个良好的开始是四舍五入到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;

You could do this with double, however you would need to round each step of the way, which is more complicated in this example. 你可以用double做到这一点,但是你需要绕过每一步,这在这个例子中更复杂。

Math.round is one way to solve this but I would not recommend it. Math.round是解决这个问题的一种方法,但我不推荐它。

another way is to use BigDeciaml which is a lot a more accurate especially when dealing with currency 另一种方法是使用BigDeciaml,这在处理货币时特别准确

Another way in which I read about recently is to work in whole numbers until the end eg work in cents instead of dollars so $1 would be 100 instead and work from there instead. 我最近阅读的另一种方式是直到最后工作,例如以美分而不是美元工作,因此$ 1将是100而不是从那里工作。

I hope these ideas help 我希望这些想法有所帮助

very intereesting video on floating points in general 一般来说,关于浮点的非常有用的视频

math api 数学api

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM