3 ms·
That does not solve the problem in general, you still may end up with fractional quantities if you, for example, calculate a 1% fee. The correct way to do it is
by danbruc 10y ago
That does not solve the problem in general, you still may end up with fractional quantities if you, for example, calculate a 1% fee. The correct way to do it is to use a decimal floating point format AND carefully pay attention to rounding.
Also note the the way you round usually depends on the context, for some calculations you want to round up, for some down, and for some in yet another way. So you perform explicit rounding with an explicit rounding mode everywhere it is required and don't assume that implicit rounding or the default rounding mode will do the right thing because they won't in general.
EDIT: To expand a bit on the context. If you, for example, charge a percentage fee you can choose between rounding up getting at least the fee you are asking for or rounding down not charging the customer more than you are asking for. Or you can try to be less biased and round either up or down depending on the fractional part. This may still require tie-breaking, either in a direction of your choice, or randomized, or by some rule like round half to even. Different choices will obviously have different biases.
While this may almost seem somewhat pedantic, such things have been exploited, see for example »Computer Capers: Tales of Electronic Thievery, Embezzlement & Fraud«. [1][2] Also note that there may be even legal regulations prescribing how you can and can not round, for example between the Euro and the replaced national currencies. [3] I also have some faint memories of a case against someone systematically exploiting rounding errors but I can not come up with a reference and may even misremember this.
[1] http://www.snopes.com/business/bank/salami.asp http://www.snopes.com/business/bank/salami.asp
[2] https://www.amazon.com/Computer-Capers-Electronic-Thievery-Embezzlement/dp/069001743X https://www.amazon.com/Computer-Capers-Electronic-Thievery-E...
[3] http://www.sysmod.com/eurofaq.htm#ROUNDING http://www.sysmod.com/eurofaq.htm#ROUNDING
- deleted 10y ago[deleted]
- deleted 10y ago[deleted]
- mikeash 10y agoWon't decimal floats still result in errors like this if the absolute value gets too large? You can avoid that by ensuring that the mantissa has enough digits to hold the largest value you'll ever encounter with enough significance to represent the smallest value you'll ever encounter, but at that point you might as well use an integer with that smallest value as the unit.
- danbruc 10y agoOf course, you can push any system to its limits and you can certainly replace your calculations with integer arithmetics. But why would you? Decimal floating point types are readily available and usually offer enough range and precision for all practical purposes. So why reinvent the wheel? And just imagine the pain you are going to go through if you have to adjust your base denomination, say because the law suddenly requires two additional digits of precision. You will have to touch every number you ever stored somewhere. Someone else in the comments mentioned arbitrary precision arithmetics, but even those don't solve all your problems magically. Not only are they comparatively slow and more cumbersome to deal with due to their variable length nature, but they still fail to correctly deal with one third, so rounding will still be required unless you manage to avoid all the bad numbers. You may then be tempted to switch to a rational number type with arbitrary precision integers as numerator and denominator. But while you now solved the problem with the one third, you still have to approximate irrational numbers and transcendental functions, for example Euler's constant, which is not unlikely to appear in interest calculations. So you still have to think about rounding, even if possibly in less places.
- mhluongo 10y agoUse fixed-point decimal math.
- tedunangst 10y agoIt also helps to only round once. Calculate and save intermediate values. fee = accountX * 0.01 accountY = accountY + fee accountX = accountX - fee vs accountY = accountY + accountX * 0.01 accountX = accountX * 0.99 I've seen a fair bit of (non financial) accounting go wrong like this, where fractions just slip through the cracks.
- mhluongo 10y agoThat is not the correct way. The correct way is to use fxed-decimal math, and pay careful attention to rounding. No floats. Not even once.
- andrepd 10y agoWhy though? What are your arguments for fixed point over decimal floating point. If you ensure a sufficiently large mantissa it will be more resilient in procedures like calculating a small percentage of a small value.
- mhluongo 10y agoThis is a fairly settled issue in finance AFAIK, but to the arguments. When doing math in finance, we need to be explicit about the precision we use, because we're counting finite things. Things like significant figures and rounding matter, and can't be ignored by imagining we have an "infinite precision" that throws away information, as floating point math does. Using fixed-point decimal math as default helps mitigate this class of mistakes in this domain. It's similar to using a memory safe language outside embedded programming- if there's no value in the other option, and it's a source of bugs, eliminate it. NB: Using fixed-point decimal math is similar to the advice to use integers, though it's a bit more flexible (eg keeping amounts in "BTC" with a fixed 8 decimal places instead of "satoshis").
- danbruc 10y agoNo floats. Not even once. Do you mean no decimal floating point numbers or did you misread my comment as advocating binary floating point numbers plus careful rounding? Fixed point decimal numbers are of course also okay, it just changes what you have to pay attention to. And often fixed point decimal numbers are in some sense secretly floating point decimal numbers, they just do not automatically adjust the decimal point [1] but require doing so explicitly. And this may of course be useful because you will run into an overflow and not accidentally lose precision when numbers become large. On the other hand you may lose precision if numbers become small and you only have a fixed precision. [1] I am not sure what the important factor is for calling a number type fixed point or floating point. Is it the ability to move the decimal point around or is it doing so automatically?