Sam Dealey
Sam Dealey

Reputation: 381

Bad result from Java modulo operator?

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

Answers (4)

petrnohejl
petrnohejl

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

Jon Skeet
Jon Skeet

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

Nayuki
Nayuki

Reputation: 18533

Arithmetic

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.

Workarounds

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.

Further reading

The topic of floating-point caveats has been covered in a variety of articles:

(In decreasing order of reader-friendliness.)

Upvotes: 2

rm5248
rm5248

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

Related Questions