2012-07-06 8 views
7

que tiene la siguiente función:C# isPowerOf función

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; 
} 

me inserta la función de impresión para su análisis.

Si llamo a la función:

isPowerOf(25, 2) 

devolverlo cierto ya 5^2 es igual a 25. Pero, si llamo 16807, que es 7^5, la siguiente manera:

isPowerOf(16807, 5) 

En este caso, imprime '7' pero a == (int)a devuelve falso.

¿Puede ayudarnos? ¡Gracias!

+6

Enlace obligatorio a [Lo que todo científico informático debe saber sobre la aritmética de coma flotante] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – AakashM

+1

Todo el mundo va a sugerir mejor comparaciones de punto flotante, pero IMO la raíz del problema es el algoritmo aquí. – harold

Respuesta

6

Trate de usar una pequeña épsilon de errores de redondeo:

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

Como sugirió Harold, que será mejor para redondear en el caso a pasa a ser ligeramente menor que el valor entero, como 3,99999:

return Math.Abs(a - Math.Round(a)) < 0.0001; 
+0

Funciona ahora, pero ¿cómo es que 7! = (Int) 7? – Novak

+0

@GuyDavid: Es debido a errores de redondeo, el número que obtuvo no es 7, pero es 7.000000001 o algo así – Dani

+0

@Guy David intente: Console.WriteLine ((int) a); –

2

Si se depura el código y luego se puede ver que en la primera comparación:

isPowerOf(25, 2) 

es la celebración de un 5.0 Aquí == 5.0 5 => es por eso que se obtiene cierto

y en segundo isPowerOf(16807, 5)

es la celebración de un 7.0000000000000009

y desde 7.0000000000000009 != 7 => que está recibiendo falsa. y Console.WriteLine (a) está truncando/redondeo al doble y sólo muestran 7

Es por ello que es necesario comparar el valor más cercano al igual que en la solución de Dani

2

Math.Pow opera sobre double s, por lo que los errores de redondeo entran en juega cuando echas raíces. Si desea comprobar que has encontrado una potencia exacta:

  • realizar la Math.Pow en la actualidad, para extraer la raíz
  • ronda el resultado al entero más cercano
  • aumento este entero en el potencia suministrada, y compruebe que obtiene el objetivo suministrado. Math.Pow habrá exacta de los números en el rango de int al subir a un entero poderes han sido sugeridas
5

comparaciones que solucionar el problema, pero lo que es realmente el problema aquí es que punto flotante no debe estar implicado en absoluto. Desea una respuesta exacta a una pregunta que involucre números enteros, no una aproximación de cálculos realizados en medidas inherentemente inexactas.

Entonces, ¿cómo más se puede hacer esto?

La primera cosa que viene a la mente es un tramposo:

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 

que va a trabajar, siempre que el guess es menor que 1 fuera. Pero no puedo garantizar que siempre funcionará para sus entradas, porque esa condición no siempre se cumple.

Así que aquí es la siguiente cosa que viene a la mente: una búsqueda binaria (la variante en la que se busca el límite superior primero) para el base en exponentiateBySquaring(base, power) para los que el resultado es más cercana a num. Si y solo si la respuesta más cercana es igual a num (y ambos son enteros, entonces esta comparación es limpia), entonces num es una potencia power. A menos que haya desbordamiento (no debería haber), eso siempre debería funcionar.

+0

Sí, sí, hay buenas razones por las cuales los números enteros y los números de coma flotante son tipos separados. –