Man nehme folgenden Code:
DROP DATABASE IF EXISTS test; CREATE DATABASE test; USE test; DROP TABLE IF EXISTS test; CREATE TABLE test (number_double DOUBLE, number_float FLOAT); INSERT INTO test VALUES (1000000000000000, 1000000000000000); SELECT number_double, number_float FROM test;
Okay, soweit noch keine Sensation. Wenn man nun aber MySQL dazu zwingt, den konkreten und exponentenfreien Wert rauszurücken, indem man folgendermaßen selektiert:
SELECT number_double * 1, number_float * 1 FROM test;
offenbart sich:
Gleiches Spiel selbstverständlich, wenn ich den Wert per PHP aus der Datenbank heraushole.
wtf???
Ja, float sollte eh nicht in mysql benutzt werden, weil es nur eine (ODBC) Hilfsfunktion ist. Wenn Du float mit einem Wert zwischen 25 und 53 benutzt, sollte das Gleiche raus kommen. Warum natürlich zwei Ausgaben raus kommen, leuchtet mir gerade auch nicht ein.
Das hat folgende Erklärung:
die Fließkommazahlen in MySQL sind über den IEEE 754 implementiert.
und das Problem liegt grundlegend daran, dass 1e15 nicht im Wertebereich des Floats liegt.
1e15 hat 34 signifikante Stellen, jedoch sind die Mantissen-Breiten wie folgt:
FLOAT: 23
DOUBLE: 52
(hängt von Hardware und Konfiguration ab, das sind jetzt die Werte der Spezifikation)
D.h. die 1e15 könnte so oder so nicht in dem FLOAT ohne Rundung gespeichert werden. MySQL schluckt dies wohl allerdings.
Sobald du allerdings mit 1 multiplizierst, wird auf der Server-Hardware eine Floating Point Multiplikation ausgeführt – der Wert ist allerdings zu groß, weswegen er gerundet wird.
Wenn wir uns nun den Wert mal genau ansehen, sehen wir, dass
999999986991104 binär dargestellt so aussieht:
11100011010111111010100100000000000000000000000000
Was genau:
„1.“ (23 Stellen Mantisse) + Exponent 2^x ist
Mehr Stellen (was du für 1e15 bräuchtest) passen nicht in den Datentyp, d.h. man bekommt Rundungsfehler.
PS: in deinem Beispielcode verwendest du 1e11, die Ausgabe ist jedoch 1e15…
PPS: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
Danke für den hervorragenden Kommentar und die Aufklärung.
Habe den Code eben korrigiert.