If you are thinking about using floating-point to help with integer arithmetics, you have to be careful.
I usually try to avoid FP calculations whenever possible.
Floating-point operations are not exact. You can never know for sure what will (int)(Math.log(65536)/Math.log(2))
evaluate to. For example, Math.ceil(Math.log(1<<29) / Math.log(2))
is 30 on my PC where mathematically it should be exactly 29. I didn't find a value for x where (int)(Math.log(x)/Math.log(2))
fails (just because there are only 32 "dangerous" values), but it does not mean that it will work the same way on any PC.
The usual trick here is using "epsilon" when rounding. Like (int)(Math.log(x)/Math.log(2)+1e-10)
should never fail. The choice of this "epsilon" is not a trivial task.
More demonstration, using a more general task - trying to implement int log(int x, int base)
:
The testing code:
static int pow(int base, int power) {
int result = 1;
for (int i = 0; i < power; i++)
result *= base;
return result;
}
private static void test(int base, int pow) {
int x = pow(base, pow);
if (pow != log(x, base))
System.out.println(String.format("error at %d^%d", base, pow));
if(pow!=0 && (pow-1) != log(x-1, base))
System.out.println(String.format("error at %d^%d-1", base, pow));
}
public static void main(String[] args) {
for (int base = 2; base < 500; base++) {
int maxPow = (int) (Math.log(Integer.MAX_VALUE) / Math.log(base));
for (int pow = 0; pow <= maxPow; pow++) {
test(base, pow);
}
}
}
If we use the most straight-forward implementation of logarithm,
static int log(int x, int base)
{
return (int) (Math.log(x) / Math.log(base));
}
this prints:
error at 3^5
error at 3^10
error at 3^13
error at 3^15
error at 3^17
error at 9^5
error at 10^3
error at 10^6
error at 10^9
error at 11^7
error at 12^7
...
To completely get rid of errors I had to add epsilon which is between 1e-11 and 1e-14.
Could you have told this before testing?
I definitely could not.