2012-07-06 14 views
7

ho la funzione successiva:C# isPowerOf funzione

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

ho inserito la funzione di stampa per l'analisi.

Se io chiamo la funzione:

isPowerOf(25, 2) 

E tornare vero in quanto 5^2 uguale a 25. Ma, se io chiamo 16807, che è 7^5, la via successiva:

isPowerOf(16807, 5) 

In questo caso, stampa '7' ma a == (int)a restituisce falso.

Potete essere d'aiuto? Grazie!

+6

Collegamento obbligatorio a [Ciò che ogni scienziato informatico dovrebbe sapere sull'aritmetica a virgola mobile] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – AakashM

+1

Ognuno suggerirà meglio confronti a virgola mobile, ma IMO la radice del problema è l'algoritmo qui. – harold

risposta

6

provare a utilizzare un piccolo epsilon per errori di arrotondamento:

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

Come suggerito harold, sarà meglio arrotondare in caso a sembra essere leggermente più piccolo del valore intero, come 3,99,999 mila:

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

Funziona ora, ma come mai 7! = (Int) 7? – Novak

+0

@GuyDavid: a causa di errori di arrotondamento, il numero ottenuto non è 7, ma è 7.000000001 o qualcosa del genere – Dani

+0

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

2

Se si esegue il debug del codice e quindi si può vedere che in primo confronto:

isPowerOf(25, 2) 

una è in possesso di 5.0 Qui 5.0 == 5 => è per questo che si ottiene vero

e nel 2 ° isPowerOf(16807, 5)

una è in possesso di 7.0000000000000009

e dal 7.0000000000000009 != 7 => si stanno ottenendo falso. e Console.WriteLine (a) è troncando/arrotondamento il doppio e mostrare solo il 7

è per questo che è necessario confrontare il valore più vicino come nella soluzione di Dani

2

Math.Pow opera sulla double s, gli errori di arrotondamento in modo da entrare in giocare quando si radicano. Se si desidera verificare che hai trovato una potenza esatta:

  • eseguire il Math.Pow come attualmente, per estrarre la radice
  • rotonda il risultato al numero intero più vicino
  • raise questo intero al fornito alimentazione e controllare di avere il target fornito. Math.Pow sarà esatta per numeri nella gamma di int durante il sollevamento a intero poteri
5

I confronti che risolvere il problema sono stati suggeriti, ma ciò che è in realtà il problema qui è che virgola mobile non dovrebbe essere coinvolto a tutti. Si desidera una risposta esatta a una domanda che coinvolge numeri interi, non un'approssimazione di calcoli eseguiti su misurazioni intrinsecamente imprecise.

Quindi, in quale altro modo può essere fatto?

La prima cosa che viene in mente è un imbroglio:

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 

che funzionerà fino a quando il guess è inferiore a 1 fuori. Ma non posso garantire che funzionerà sempre per i tuoi input, perché questa condizione non è sempre soddisfatta.

Quindi, ecco la prossima cosa che viene in mente: una ricerca binaria (la variante in cui si cerca il limite superiore prima) per la base in exponentiateBySquaring(base, power) per cui il risultato è più vicino al num. Se e solo se la risposta più vicina è uguale a num (e sono entrambi interi, quindi questo confronto è pulito), quindi num è una potenza di power. A meno che non ci sia un overflow (non dovrebbe esserlo), dovrebbe sempre funzionare.

+0

Sì, sì, ci sono buone ragioni per cui numeri interi e numeri a virgola mobile sono separati. –