Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modulo operator gives unexpected output in Java

I have the following, working method in Java:

/**
 * Determines if n is a power of z
 * 
 * @param z the number that n may be a power of
 * @param n the number that may be a power of z
 * @return true if n is a power of z 
 */
public boolean isPowerOf(int z, int n) {
    double output = Math.log(n) / Math.log(z);
    if(output % 1 > 0) {
        return false;
    } else {
        return true;
    }
}

isPowerOf(3, 729); //returns true, because 3^6 = 729

Works fine n mighty, but I tried it differently the first time:

public boolean isPowerOf(int z, int n) {
    double output = Math.log(n) % Math.log(z);
    if(output != 0) {
        return false;
    } else {
        return true;
    }
}

However, for log(729) % log(3) seems to return 1.0986122886681093, while the outcome of log(729) / log(3) is 6.

Anyone able to tell me what causes the modulo operator to still give 1.09 remainder here?

like image 274
Mark Tielemans Avatar asked Jan 15 '23 06:01

Mark Tielemans


1 Answers

Anyone able to tell me what causes the modulo operator to still give 1.09 remainder here?

The normal floating point inaccuracies, basically. The values you're using aren't exactly log(729) and log(3). If you look at log(3) and log(729) % log(3) you'll see they're almost exactly the same:

public class Test {
    public static void main(String[] args) {
        double x = Math.log(729);
        double y = Math.log(3);
        System.out.println(x);
        System.out.println(y);
        System.out.println(x % y);
    }
}

Output:

6.591673732008658
1.0986122886681098
1.0986122886681093

In other words, log(729) is effectively log(3) * 5.9999999999999 (or something similar). You'll probably want to add some tolerance to your test, basically, and return whether or not the remainder is very close to 0 or very close to the log(z).

Alternatively, use log and division to work out "roughly" what the power should be, then Math.pow to check for the exact value:

int power = (int) (Math.log(n) / Math.log(z) + 0.5);
return n == Math.pow(z, power);

Here you should be okay in terms of floating point inaccuracies until the numbers get "pretty big". You can use BigInteger if you want to cope with very big numbers precisely.

like image 112
Jon Skeet Avatar answered Jan 29 '23 07:01

Jon Skeet