7

I have the next function:

static bool isPowerOf(int num, int power)
{
        double b = 1.0 / power;
        double a = Math.Pow(num, b);
        Console.WriteLine(a);
        return a == (int)a;
}

I inserted the print function for analysis.

If I call the function:

isPowerOf(25, 2)

It return true since 5^2 equals 25. But, if I call 16807, which is 7^5, the next way:

isPowerOf(16807, 5)

In this case, it prints '7' but a == (int)a return false.

Can you help? Thanks!

madhairsilence
  • 3,787
  • 2
  • 35
  • 76
Novak
  • 2,760
  • 9
  • 42
  • 63
  • 6
    Obligatory link to [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – AakashM Jul 06 '12 at 09:47
  • 1
    Everyone's going to suggest better floating point comparisons, but IMO the root of the problem is the algorithm here. – harold Jul 06 '12 at 09:48

4 Answers4

6

Try using a small epsilon for rounding errors:

return Math.Abs(a - (int)a) < 0.0001;

As harold suggested, it will be better to round in case a happens to be slightly smaller than the integer value, like 3.99999:

return Math.Abs(a - Math.Round(a)) < 0.0001;
Daniel
  • 30,896
  • 18
  • 85
  • 139
  • @GuyDavid: Its because of rounding errors, the number you got isn't 7, but it is 7.000000001 or something like that – Daniel Jul 06 '12 at 09:46
  • @Guy David try : Console.WriteLine((int)a); – Nahuel Fouilleul Jul 06 '12 at 09:50
  • What if the result of math.pow is off by more than 0.0001? – harold Jul 06 '12 at 10:11
  • @harold: it shouldn't be, rounding errors are usually at a constant point if you make a single calculation. – Daniel Jul 06 '12 at 10:34
  • @Dani maybe so, but this still breaks. For example, for `num = 4*4*4` `pow = 3`, you get `a = 3.9999999999999996` which is *truncated down*, with a difference of nearly one. Should probably be a round rather than a truncate. – harold Jul 06 '12 at 10:50
5

Comparisons that fix the issue have been suggested, but what's actually the problem here is that floating point should not be involved at all. You want an exact answer to a question involving integers, not an approximation of calculations done on inherently inaccurate measurements.

So how else can this be done?

The first thing that comes to mind is a cheat:

double guess = Math.Pow(num, 1.0 / power);
return num == exponentiateBySquaring((int)guess, power) ||
       num == exponentiateBySquaring((int)Math.Ceil(guess), power);
       // do NOT replace exponentiateBySquaring with Math.Pow

It'll work as long as the guess is less than 1 off. But I can't guarantee that it will always work for your inputs, because that condition is not always met.

So here's the next thing that comes to mind: a binary search (the variant where you search for the upper boundary first) for the base in exponentiateBySquaring(base, power) for which the result is closest to num. If and only if the closest answer is equal to num (and they are both integers, so this comparison is clean), then num is a power-th power. Unless there is overflow (there shouldn't be), that should always work.

harold
  • 61,398
  • 6
  • 86
  • 164
2

If you debug the code and then you can see that in first comparison:

isPowerOf(25, 2) 

a is holding 5.0 Here 5.0 == 5 => that is why you get true

and in 2nd isPowerOf(16807, 5)

a is holding 7.0000000000000009

and since 7.0000000000000009 != 7 => you are getting false. and Console.WriteLine(a) is truncating/rounding the double and only show 7

That is why you need to compare the nearest value like in Dani's solution

Habib
  • 219,104
  • 29
  • 407
  • 436
2

Math.Pow operates on doubles, so rounding errors come into play when taking roots. If you want to check that you've found an exact power:

  • perform the Math.Pow as currently, to extract the root
  • round the result to the nearest integer
  • raise this integer to the supplied power, and check you get the supplied target. Math.Pow will be exact for numbers in the range of int when raising to integer powers
AakashM
  • 62,551
  • 17
  • 151
  • 186