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”;

17 Bemerkungen zu “Ich hasse PHP”

  1. md

    Erwarten würde ich 3405.
    Bekommen tu ich 3404.
    Das ist allerdings seltsam.

  2. md

    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?

  3. Klaus Keppler

    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…

  4. marmota

    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.

  5. Klaus Keppler

    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.

  6. marmota

    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 :-)

  7. Master of Bachelor

    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 ;)

  8. Klaus Keppler

    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.

  9. Master of Bachelor

    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!

  10. Klaus Keppler

    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.

  11. Master of Bachelor

    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

  12. Klaus Keppler

    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

  13. Patrick

    $betrag = ‘34.04148′;

    Wieso packst du die Zahl eigentlich in einen String?

  14. Klaus Keppler

    Es handelt sich nur um Beispielcode…
    Im “echten” Code findet auch die ganze Berechnung in nur einer Zeile statt.

  15. dog

    Wenn man das ganze aber nun mit BCMath und 10 Nachkommastellen rechnet passiert folgendes:

    Input: 34.04148
    Schritt 1: 680.82960
    Schritt 2: 681
    Schritt 3: 34.0500000000
    Schritt 4: 3405.0000000000
    string(15) “3405.0000000000″
    Schritt 5: 3405

  16. niemand

    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

  17. Klaus Keppler

    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. :-)

Einen Kommentar schreiben