Reputation: 381
I suspect this has been asked before, but can't seem to find a question that matches...
I'm using Scala, but I'm pretty sure this is just a Java problem... input values are doubles
println(28.0 / 5.6)
println(28.0 % 5.6)
The result of these lines is
5.0
1.7763568394002505E-15
Which means that Java is performing the division correctly, but for some reason getting the modulo wrong, since the modulo should be 0 for any division problem that resolves to a whole number...
Is there a workaround for this?
Thanks!
Upvotes: 5
Views: 7912
Reputation: 7759
This is my solution for check if double value is divisible by another using modulo operator:
public class DoubleOperation
{
public static final double EPSILON = 0.000001d;
public static boolean equals(double val1, double val2)
{
return (Math.abs(val1 - val2) < EPSILON);
}
public static boolean divisible(double dividend, double divisor)
{
double divisionRemainder = dividend % divisor;
return (equals(divisionRemainder, 0.0d) || equals(divisionRemainder, divisor));
}
}
Upvotes: 0
Reputation: 1499760
The 5.0 just shows that the precise result as Java understands it is closer to 5.0 than it is to any other double. That doesn't mean the precise result of the operation is exactly 5.
Now when you ask for the modulus, you're able to down to a much finer level of detail, because the result isn't pinned to having the "5" part.
That's not a great explanation, but imagine you had a decimal floating point type with 4 digits of precision. What's the result of 1000 / 99.99 and 1000 % 99.99?
Well, the real result starts with 10.001001 - so you have to round that to 10.00. However, the remainder is 0.10, which you can express. So again, it looks like the division gives you a whole number, but it doesn't quite.
With that in mind, bear in mind that your literal of 5.6 is actually 5.5999999999999996447286321199499070644378662109375. Now clearly 28.0 (which *can) be represented exactly divided by that number isn't exactly 5.
EDIT: Now if you perform the result with decimal floating point arithmetic using BigDecimal
, the value really is exactly 5.6, and there are no problems:
import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("28.0");
BigDecimal y = new BigDecimal("5.6");
BigDecimal div = x.divide(y);
BigDecimal rem = x.remainder(y);
System.out.println(div); // Prints 5
System.out.println(rem); // Prints 0.0
}
}
Upvotes: 14
Reputation: 18533
First of all, the decimal number 5.6 cannot be represented exactly in binary floating-point. It is rounded to the exact binary fraction 3152519739159347/249.
The fact that 28.0 / 5.6 = 5.0 is because 5.0 is the closest double
number to the true result, where the true result is 5.0000000000000003172065784643....
As for 28.0 % 5.6, the true result is exactly 1/249, which is approximately 1.776 × 10−15, so the calculation is correctly rounded.
Why do you need a workaround? For most applications, keeping the very slightly wrong result is fine. Are you concerned about displaying "pretty" results?
If you need absolutely precise arithmetic, then you will need to use some implementation of BigFraction.
The topic of floating-point caveats has been covered in a variety of articles:
(In decreasing order of reader-friendliness.)
Upvotes: 2
Reputation: 2635
The result isn't actually 0, but it's pretty close( 0.00000000000000177635... ). The problem is that some decimal numbers can't be represented exactly in binary, so that's where the issue is coming in; I'd suspect that the same result would be printed out in C/C++.
Upvotes: 3