Every now and then I see some rounding errors which are caused by floor'ing some values as shown in the two examples below.
// floor(number, precision)
double balance = floor(0.7/0.1, 3) // = 6.999
double balance = floor(0.7*0.1, 3) // = 0.069
The problem of course is 0.7/0.1
and 0.7*0.1
are not exactly the number it should be due to representation errors [check the NOTE below].
One solution could be to add an epsilon so any representation error is mitigated just before applying the floor.
double balance = floor(0.7/0.1 + 1e-10, 3) // = 7.0
double balance = floor(0.7*0.1 + 1e-10, 3) // = 0.07
What epsilon should I use so it's guaranteed to work in all cases? I feel this solution is rather hacky unless I have a good strategy for choosing the correct epsilon which probably depends on the numbers I'm dealing with.
For instance, if there was a way of getting an estimation of the error (as in representation - number
) or at least the sign of it (whether representation > number
or not), that would be helpful to determine in what direction I should correct the result before applying the floor
.
Any other workaround you can think of is very welcome.
NOTE: I know the real problem is I'm using doubles and it has representation errors. Please refrain from saying anything like I should store the balance in a long ((long) Math.floor(3931809L/0.080241D)
is equally erratic). I also tried using BigDecimal but the performance degraded quite a lot (it's a realtime application). Also, note I'm not very concerned about propagating small errors over time, I do a lot of calculations like those above but I start from a fresh balance number every time (I do maybe 3 of those operations before returning and starting over).
EDIT: To make that clear, that's the only operation I do, and I repeat it 3 times on the same balance. For example, I take a balance in USD and I convert it to RUB, then to JPY then to EUR, and I return the balance and start over from the beginning (with a fresh balance number, ie no rounding error is propagated other than on these 3 operations). The values are not constrained apart from them being positive numbers (ie, in the range [0, +inf)) and the precision is always below 8 (8 decimal digits, ie 0.00000001 is the smallest balance I will ever have to deal with).