Ich hasse PHP
Das ich kein großer Freund von PHP bin ist wahrscheinlich nicht neu. Inzwischen fällt es mir aber schwer, PHP überhaupt als Programmiersprache ernst zu nehmen.
Wer mir zuerst sagen kann, welches Ergebnis man denn nach Schritt 5 erwartet (und welches man tatsächlich bekommt), gewinnt einen Kugelschreiber.
$betrag = ‘34.04148′; # Betrag in CHF
echo “Input: $betrag\n”;# Schritt 1: *20
$betrag = $betrag * 20;
echo “Schritt 1: $betrag\n”;# Schritt 2: Rundung
$betrag = round($betrag);
echo “Schritt 2: $betrag\n”;# Schritt 3: / 20
$betrag = $betrag / 20;
echo “Schritt 3: $betrag\n”;# Schritt 4: * 100
$betrag = $betrag * 100;
echo “Schritt 4: $betrag\n”;
var_dump($betrag);# Schritt 5: intval
$betrag = intval($betrag);
echo “Schritt 5: $betrag\n”;
Am 9. April 2009 um 10:06 Uhr
Erwarten würde ich 3405.
Bekommen tu ich 3404.
Das ist allerdings seltsam.
Am 9. April 2009 um 10:07 Uhr
Nachtrag: allerdings kapier ich das nicht: In Schritt 4 steht ja 3405 drin, zumindest nach dem, was var_dump sagt. Warum mach intval dann ein 3404 draus?
Am 9. April 2009 um 10:12 Uhr
Glückwunsch.
Tjo, auch wenn PHP sogar mit var_dump “3405″ anzeigt, rechnet es intern mit “3404.999999999999…”.
Einfach mal “ini_set(’precision’, 50);” am Anfang einfügen.
PHP ist wohl nur zum HTML-Code-Basteln gedacht, und nicht für mathematische Operationen jenseits der Dezimalgrenze…
Am 9. April 2009 um 10:17 Uhr
Da bin ich jetzt zwei Minuten zu spät gekommen…
Fairerweise muss man aber auch sagen, daß auch in anderen Sprachen bei solchen Operationen Vorsicht geboten ist.
Am 9. April 2009 um 10:22 Uhr
In welchen Sprachen denn noch?
Dieses Problem hängt scheinbar nur mit der Ausgabegenauigkeit von Fließkommazahlen zusammen. In C, Perl und Java passiert sowas jedenfalls nicht.
Am 9. April 2009 um 10:29 Uhr
Das spezielle Verhalten ist in PHP wirklich dämlich, gar keine Frage.
Ich meinte nur, daß man sich auch in anderen Sprachen sehr genaue Gedanken machen sollte, wenn man so mit Werten rumschachert
Am 9. April 2009 um 10:35 Uhr
Gibts in jeder Sprache solche merkwürdigen Berechnungen!
In Java hat man es (zumindest lange Zeit) hinbekommen, dass ein
a-b==0 (mit a und b floats und jeweils gleich 1) false war. War das selbe Problem mit den Nachkommastellen.
Den genauen Code hab ich nicht mehr im Kopf. Man brauchte aber deutlich weniger Zeilen um das Ergebnis hinzubekommen
Am 9. April 2009 um 10:41 Uhr
Nein, das mit Java ist ein anderes Problem (Ungenauigkeit bei Float-Berechnungen) - das konnte einem sogar mit alten Pentium-CPUs passieren (egal mit welcher Programmiersprache).
PHP macht den Fehler, dass es eigenständig die Zahlen mit einer anderen Genauigkeit ausgibt, als es für interne Berechnungen verwendet.
Am 9. April 2009 um 11:09 Uhr
Und damit hast du exakt gesagt, was PHP macht! PHP hat das selbe Problem wie Java bei der 1-1-Float-Problematik!
Gebe nach jeder Zeile per var_dump die Variable aus, Ergebnis:
Input: $betrag\nstring(8) “34.04148″
Schritt 1: 680.8296
float(680.8296)
Schritt 2: 681
float(681)
Schritt 3: 34.05
float(34.05)
Schritt 4: 3405
float(3405)
Schritt 5: 3404
int(3404)
Was man sieht ist das selbe Float-Ungenauigkeits-Problem! Das Runden ändert den Variablentyp (natürlich) nicht auf int, sondern er bleibt bei float. In dem Moment wo du es zu int castest (und intval macht nichts anderes) fällt die Ungenauigkeit ins Gewicht.
Bei meinem 1-1-Java-Beispiel konnte man die Variablen auch ausgeben und Java behauptete auch da, dass beide Variablen den Wert 1 haben und eben nicht 0,9999…99 bzw. 1,0000…01!
Daher immer beim casten von float auf int: intval(round($var))!
Das selbe gilt für jede andere Programmiersprache!
Am 9. April 2009 um 11:22 Uhr
Sorry, ich schätze mir meinten das gleiche. Auf was ich aber hinaus will: wenn eine Variable intern den Wert “34.049999999″ hat, will ich spätestens mit var_dump() oder sprintf(”0.20f”) genau diesen Wert sehen (was PHP aber nicht macht).
Ich habe gerade kein JDK griffbereit, behaupte aber mal, dass Java beim PrintfFormat().sprintf() die Zahl tatsächlich “vollständig” ausgeben würde - unabhängig von der internen Rechenungenauigkeit.
Am 9. April 2009 um 12:22 Uhr
wenn PHP den internen Wert anzeigt, dann ist es auch verkehrt, da das ja nicht der Wert ist, dem die Variable zugeordnet wurde.
PHP zeigt genau den Wert an, den die Variable haben sollte. Durch die interne Ungenauigkeit kommt es beim Abschneiden der Nachkommastellen dann leider zu dem Ungenauigkeitsfehler.
Wäre aber natürlich schöner, wenn PHP bei einem intval intern von alleine ein floor macht.
Zu der Problematik siehe auch PHP Dokumentation: http://de2.php.net/manual/de/language.types.float.php#warn.float-precision
Am 9. April 2009 um 13:23 Uhr
Letztendlich verwirrt die Tatsache, dass sich “intval” nicht an der Ausgabegenauigkeit orientiert, sondern lediglich ein Synonym für “floor” ist (und sich somit auf die intern verwendete Genauigkeit bezieht).
Rein arithmetisch verhält sich o.g. Beispiel in Perl oder C natürlich genauso, und Schritt 5 würde (bei floor() statt intval()) auch “3404″ ausgeben. Nur würde ich das da auch so erwarten.
http://de.wikipedia.org/wiki/Principle_Of_Least_Surprise
Am 9. April 2009 um 13:53 Uhr
$betrag = ‘34.04148′;
Wieso packst du die Zahl eigentlich in einen String?
Am 9. April 2009 um 14:02 Uhr
Es handelt sich nur um Beispielcode…
Im “echten” Code findet auch die ganze Berechnung in nur einer Zeile statt.
Am 10. April 2009 um 00:05 Uhr
Wenn man das ganze aber nun mit BCMath und 10 Nachkommastellen rechnet passiert folgendes:
Am 29. September 2010 um 15:08 Uhr
Bin hier per google drauf gestoßen..
Dieses Problem hier ist lediglich ein Darstellungsproblem (und der Fehler ist wohl eher, dass PHP die Zahlen bei der Darstellung rundet, sogar bei var_dump - das ist zugegeben verwirrend).
Aber mal ehrlich: man benutzt für Preisberechnungen keine Floating Point Zahlen! Niemals. Dummerweise wird das trotzdem viel zu oft gemacht… und da redet man und erklärt man, und am Ende machen sie den Fehler doch immer wieder - ging mir gerade bei einem Projekt so
Es hilft nur lesen, lernen, verstehen:…
http://www.c2.com/cgi/wiki?FloatingPointCurrency
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Am 12. Oktober 2010 um 14:25 Uhr
Die Links zu dem Thema sind wirklich interessant.
Problematisch ist aber, dass man bei schwach typisierten Sprachen wie PHP und Perl nunmal keinen Einfluss darauf hat, in welchem internen Datentyp die Zahlen abgelegt werden.
Und wenn dann noch die Darstellung intern irgendwie herumrundet (obwohl man das explizit nicht wünscht), wird’s echt blöd.