2013-03-11 1 views
1

Je travaillais sur un projet personnel dans lequel j'avais besoin de déterminer tous les prime powers entre 0 et 999. Parce que je ne suis pas particulièrement bon en maths, j'ai fatigué l'approche suivante de la force brutale.La fonction math.h pow() ne fonctionne pas correctement?

bool constexpr is_prime(int imp) 
{ 
    return imp == 1 ? false : (imp % imp == 0 && imp % 1 == 0 && [&imp]{ for(int i = 2; i < imp; ++i) if(imp % i == 0) return false; return true;}()); 
} 

bool is_prime_power(int imp) 
{ 
    for(int i = 1; i < 1000; ++i) 
     if (is_prime(i)) 
      for (int j = 0; j < 100; ++j) 
       if (imp == pow(i, j)) 
        return true; 
    return false; 
} 

Pour 0 ... 30 la sortie doit être (selon A000961):

1 2 3 4 5 7 8 9 11 13 16 17 19 

Cependant, voici ce que je reçois à la place:

1 2 3 4 5 7 8 9 11 16 19 

Où avez 13 et 17 disparaissent à?

Comme je n'ai pas trouvé de problèmes logiques avec mon approche, j'ai implémenté ma propre fonction pow().

double constexpr _pow(double base, double exp) 
{ 
    return exp == 0 ? 1 : base*pow(base, exp - 1); 
} 

Maintenant, si je l'appelle ma version de _pow() au lieu de pow() à partir math.h la sortie est affichée comme exceptée. Ma mise en œuvre est-elle incorrecte? Sinon, pow() de math.h ne peut pas fonctionner correctement. Une idée de ce qui cause cela?

+5

* Jamais * comparer un double à un int. –

+0

@Hans Passant C'est la première fois que j'ai entendu quelque chose comme ça. Quel est le problème? Le compilateur ne devrait-il pas m'avertir de ce genre de choses? – CaffeineAddict

+2

@ cyberpunk_ http://floating-point-gui.de/ –

Répondre

3

Le problème est que double (et les nombres à virgule flottante en général) ne sont pas exacts et les fonctions mathématiques de la bibliothèque standard utilisent également des formules approximatives pour effectuer les calculs. Si vous appelez pow(2, 10), vous pouvez obtenir 1023.999375, par exemple (qui est ensuite, selon le contexte, peut être tronqué à 1023).

Ainsi, au lieu d'utiliser la fonction à virgule flottante pow() de la bibliothèque standard, vous pouvez aller avec votre propre, implémentation exacte pour les entiers:

int constexpr _pow(int base, int exp) 
{ 
    return exp == 0 ? 1 : base * _pow(base, exp - 1); 
} 

(ou le modifier à tout type intégral vous voulez si vous avez besoin de unsigned ou long long).

+0

Fait sens jusqu'ici. Mais pourquoi ma mise en œuvre qui retourne STILL semble-t-elle fonctionner? – CaffeineAddict

+2

@cyberpunk_, votre version fonctionne parce que tous les doublons que vous traitez ont été convertis à partir d'entiers. La fonction de bibliothèque mathématique 'pow' utilise des calculs plus rapides, mais elle nécessite des opérations à virgule flottante qui perdent un peu de précision. –

+0

Une façon d'implémenter pow (x, y) est exp (y * ln (x)) et à la fois ln() et exp() peuvent provoquer des erreurs d'arrondi. (Je suis sûr que le vrai pow() n'utilise pas cette méthode.) –

1

Vous n'avez jamais besoin de vérifier qu'un nombre se divise et que l'on divise un nombre. Ceci est inutile: imp % imp == 0 && imp % 1 == 0

Sinon, le problème que vous avez est parce que pow renvoie un double ou flotter pendant que vous le comparez à un entier. La comaparison peut échouer en raison de l'erreur d'arrondi. Je vous suggère de mettre en œuvre votre propre opération de puissance entière afin d'éviter toute erreur d'arrondi. Vous pouvez également convertir i en double et utiliser la comparaison avec une petite tolérance epsylon.

Questions connexes