Je crypte l'entrée de l'utilisateur pour générer une chaîne de mot de passe. Mais une ligne de code donne des résultats différents dans différentes versions du framework. Code partiel avec valeur de touche enfoncée par l'utilisateur:
Touche enfoncée: 1. La variable ascii
est 49. Valeur de «e» et «n» après quelques calculs:
e = 103,
n = 143,
Math.Pow(ascii, e) % n
Résultat du code ci-dessus:
Dans .NET 3.5 (C #)
Math.Pow(ascii, e) % n
donne
9.0
.Dans .NET 4 (C #)
Math.Pow(ascii, e) % n
donne
77.0
.
Math.Pow()
donne le (même) résultat correct dans les deux versions.
Quelle est la cause et y a-t-il une solution?
%
avec des nombres à virgule flottante.Réponses:
Math.Pow
fonctionne sur les nombres à virgule flottante double précision; ainsi, vous ne devez pas vous attendre à ce que les 15 à 17 premiers chiffres du résultat soient précis:Cependant, l'arithmétique modulo nécessite que tous les chiffres soient précis. Dans votre cas, vous calculez 49103 , dont le résultat se compose de 175 chiffres, ce qui rend l'opération modulo sans signification dans vos deux réponses.
Pour déterminer la valeur correcte, vous devez utiliser l'arithmétique de précision arbitraire, telle que fournie par la
BigInteger
classe (introduite dans .NET 4.0).Edit : Comme indiqué par Mark Peters dans les commentaires ci-dessous, vous devez utiliser la
BigInteger.ModPow
méthode, qui est destinée spécifiquement à ce type d'opération:la source
1.0 - 0.9 - 0.1 == 0.0
évaluation defalse
.Outre le fait que votre fonction de hachage n'est pas très bonne * , le plus gros problème avec votre code n'est pas qu'elle renvoie un nombre différent en fonction de la version de .NET, mais que dans les deux cas elle renvoie un nombre totalement dénué de sens: la bonne réponse au problème est
49 103 Mod = 143 est 114. ( lien vers Wolfram Alpha )
Vous pouvez utiliser ce code pour calculer cette réponse:
La raison pour laquelle votre calcul produit un résultat différent est que pour produire une réponse, vous utilisez une valeur intermédiaire qui supprime la plupart des chiffres significatifs du nombre 49103 : seuls les 16 premiers de ses 175 chiffres sont corrects!
Les 159 chiffres restants sont tous faux. L'opération de mod, cependant, recherche un résultat qui nécessite que chaque chiffre soit correct, y compris les tout derniers. Par conséquent, même la moindre amélioration de la précision
Math.Pow
peut avoir été implémentée dans .NET 4 entraînerait une différence drastique de votre calcul, qui produit essentiellement un résultat arbitraire.* Puisque cette question parle d'élever des nombres entiers à des puissances élevées dans le contexte du hachage de mot de passe, il peut être une très bonne idée de lire ce lien de réponse avant de décider si votre approche actuelle doit être modifiée pour une meilleure.
la source
Ce que vous voyez est une erreur d'arrondi en double.
Math.Pow
fonctionne avec double et la différence est comme ci-dessous:.NET 2.0 et 3.5 =>
var powerResult = Math.Pow(ascii, e);
renvoie:.NET 4.0 et 4.5 =>
var powerResult = Math.Pow(ascii, e);
renvoie:Notez le dernier chiffre avant
E
et cela cause la différence dans le résultat. Ce n'est pas l'opérateur de module(%)
.la source
La précision en virgule flottante peut varier d'une machine à l'autre, et même sur la même machine .
Par conséquent, vous ne devriez pas vous y fier pour produire des résultats cohérents. Pour le chiffrement, utilisez les classes fournies par le Framework plutôt que de déployer les vôtres.
la source
Il y a beaucoup de réponses sur la façon dont le code est mauvais. Cependant, pourquoi le résultat est différent…
Les FPU d'Intel utilisent le format 80 bits en interne pour obtenir plus de précision pour les résultats intermédiaires. Donc, si une valeur est dans le registre du processeur, elle obtient 80 bits, mais lorsqu'elle est écrite dans la pile, elle est stockée à 64 bits .
Je m'attends à ce que la nouvelle version de .NET ait un meilleur optimiseur dans sa compilation Just in Time (JIT), donc elle garde une valeur dans un registre plutôt que de l'écrire dans la pile puis de la relire à partir de la pile.
Il se peut que le JIT puisse maintenant renvoyer une valeur dans un registre plutôt que sur la pile. Ou transmettez la valeur à la fonction MOD dans un registre.
Voir aussi Question sur le débordement de pile Quels sont les applications / avantages d'un type de données de précision étendue 80 bits?
D'autres processeurs, par exemple l'ARM, donneront des résultats différents pour ce code.
la source
Il est peut-être préférable de le calculer vous-même en utilisant uniquement l'arithmétique des nombres entiers. Quelque chose comme:
Vous pouvez comparer les performances avec les performances de la solution BigInteger publiées dans les autres réponses.
la source