Ich verschlüssele die Benutzereingabe zum Generieren einer Zeichenfolge für das Kennwort. Eine Codezeile führt jedoch in verschiedenen Versionen des Frameworks zu unterschiedlichen Ergebnissen. Teilcode mit Wert der vom Benutzer gedrückten Taste:
Taste gedrückt: 1. Variable ascii
ist 49. Wert von 'e' und 'n' nach einiger Berechnung:
e = 103,
n = 143,
Math.Pow(ascii, e) % n
Ergebnis des obigen Codes:
In .NET 3.5 (C #)
Math.Pow(ascii, e) % n
gibt
9.0
.In .NET 4 (C #)
Math.Pow(ascii, e) % n
gibt
77.0
.
Math.Pow()
gibt in beiden Versionen das richtige (gleiche) Ergebnis.
Was ist die Ursache und gibt es eine Lösung?
%
Gleitkommazahlen verbieten .Antworten:
Math.Pow
arbeitet mit Gleitkommazahlen mit doppelter Genauigkeit; Daher sollten Sie nicht mehr als das erwartenDaher ersten 15 bis 17 Stellen des Ergebnisses korrekt sind:Für die Modulo-Arithmetik müssen jedoch alle Ziffern genau sein. In Ihrem Fall berechnen Sie 49 103 , deren Ergebnis aus 175 Ziffern besteht, wodurch die Modulo-Operation in beiden Antworten bedeutungslos wird.
Um den richtigen Wert zu ermitteln, sollten Sie eine Arithmetik mit beliebiger Genauigkeit verwenden, wie sie von der
BigInteger
Klasse bereitgestellt wird (eingeführt in .NET 4.0).Bearbeiten : Wie von Mark Peters in den Kommentaren unten ausgeführt, sollten Sie die
BigInteger.ModPow
Methode verwenden, die speziell für diese Art von Operation vorgesehen ist:quelle
1.0 - 0.9 - 0.1 == 0.0
Auswertung von zu vermeidenfalse
.Abgesehen von der Tatsache, dass Ihre Hashing-Funktion nicht sehr gut ist * , besteht das größte Problem mit Ihrem Code nicht darin, dass er je nach .NET-Version eine andere Nummer zurückgibt, sondern in beiden Fällen eine völlig bedeutungslose Nummer: Die richtige Antwort auf das Problem lautet
49 103 mod 143 = ist 114. ( Link zu Wolfram Alpha )
Mit diesem Code können Sie diese Antwort berechnen:
Der Grund, warum Ihre Berechnung ein anderes Ergebnis liefert, ist, dass Sie zur Erstellung einer Antwort einen Zwischenwert verwenden, der die meisten signifikanten Ziffern der Zahl 49 103 löscht: Nur die ersten 16 der 175 Ziffern sind korrekt!
Die restlichen 159 Ziffern sind alle falsch. Die Mod-Operation sucht jedoch ein Ergebnis, bei dem jede einzelne Ziffer korrekt sein muss, einschließlich der allerletzten. Daher würde selbst die kleinste Verbesserung der Genauigkeit
Math.Pow
, die möglicherweise in .NET 4 implementiert wurde, zu einem drastischen Unterschied Ihrer Berechnung führen, der im Wesentlichen zu einem beliebigen Ergebnis führt.* Da es in dieser Frage darum geht, Ganzzahlen im Zusammenhang mit dem Passwort-Hashing auf hohe Potenzen zu bringen, ist es möglicherweise eine sehr gute Idee, sie zu lesen diesen Antwortlink , bevor Sie entscheiden, ob Ihr aktueller Ansatz durch einen potenziell besseren geändert werden soll.
quelle
Was Sie sehen, ist ein doppelter Rundungsfehler.
Math.Pow
funktioniert mit double und der Unterschied ist wie folgt:.NET 2.0 und 3.5 =>
var powerResult = Math.Pow(ascii, e);
gibt Folgendes zurück:.NET 4.0 und 4.5 => geben
var powerResult = Math.Pow(ascii, e);
Folgendes zurück:Beachten Sie die letzte Ziffer zuvor
E
, die den Unterschied im Ergebnis verursacht. Es ist nicht der Moduloperator(%)
.quelle
Die Gleitkommapräzision kann von Maschine zu Maschine und sogar auf derselben Maschine variieren .
Daher sollten Sie sich nicht darauf verlassen, um konsistente Ergebnisse zu erzielen. Verwenden Sie für die Verschlüsselung die vom Framework bereitgestellten Klassen, anstatt Ihre eigenen zu rollen.
quelle
Es gibt viele Antworten darüber, wie schlecht der Code ist. Warum ist das Ergebnis jedoch anders?
Intels FPUs verwenden intern das 80-Bit- Format, um mehr Präzision für Zwischenergebnisse zu erzielen. Wenn sich also ein Wert im Prozessorregister befindet, erhält er 80 Bit, aber wenn er in den Stapel geschrieben wird, wird er bei 64 Bit gespeichert .
Ich gehe davon aus, dass die neuere Version von .NET einen besseren Optimierer in der JIT-Kompilierung (Just in Time) hat, sodass ein Wert in einem Register gespeichert wird, anstatt ihn in den Stapel zu schreiben und dann vom Stapel zurückzulesen.
Es kann sein, dass die JIT jetzt einen Wert in einem Register anstatt auf dem Stapel zurückgeben kann. Oder übergeben Sie den Wert in einem Register an die MOD-Funktion.
Siehe auch Frage zum Stapelüberlauf Was sind die Anwendungen / Vorteile eines 80-Bit-Datentyps mit erweiterter Genauigkeit?
Andere Prozessoren, z. B. der ARM, liefern für diesen Code andere Ergebnisse.
quelle
Vielleicht ist es am besten, es selbst mit nur ganzzahliger Arithmetik zu berechnen. Etwas wie:
Sie können die Leistung mit der Leistung der BigInteger-Lösung vergleichen, die in den anderen Antworten angegeben ist.
quelle