lelleck: INT-Funktion rechnet falsch

Hallo zusammen,
habe ein grosses Problem.
Habe einen kleinen Onlineshop in PHP programmiert. Hier werden die Preise der Artikel aus einer MySQL Datenbank gezogen, und in PHP Sessions zwischengespeichert (von Artikeln im Warenkorb). Das Feld in MySQL ist als decimal 5,2 angelegt.

Irgendwann habe ich festgestellt, das PHP bei einem Preis von z.B. 0,44 Euro den Wert 0.43999999999999999953525523 in der PHP Session ablegt.
Meistens hat PHP die Werte dann später beim rechnen wieder korrekt gerundet, manchmal gab es aber auch komische effekte.

Daher bin ich hingegangen und habe die Beträge mit 100 multipliziert und mit (int) in einen Integer umgewandelt. Nun hat PHP auch die Preise in Cent sauber und korrekt in die Sessions geschrieben, und auch korrekt weiter berechnet.

Allerdings habe ich nun einen Artikel, der kostet 0,29 Euro. Sobald ich dieses (und das funktioniert eben NUR bei diesem Artikel) in den Warenkorb lege, wird er nur mit 28 Cent berechnet. 1 Cent unterschlägt mir PHP.

Habe durch debugging nun rausgefunden, das erst nach dem int der Fehler auftritt. Daher habe ich folgenden kleinen Test programmiert:

  
for($i=0.00;$i<5;$i=$i+0.01)  
{  
echo "I = $i<br>";  
echo "INT(i) = " . (int)(($i)*100) . "<br>";  
echo "INTVAL(i) = " . intval($i*100) . "<br><br>";  
}  

Und das kuriose ist:
So ertwarte ich das ergebnis:

I = 1.83
INT(i) = 183
INTVAL(i) = 183

I = 1.84
INT(i) = 184
INTVAL(i) = 184

I = 1.85
INT(i) = 185
INTVAL(i) = 185

I = 1.86
INT(i) = 186
INTVAL(i) = 186

Irgendwann fängt aber PHP an sich zu verrechnen, und dann kommt sowas raus:

I = 2.84
INT(i) = 283
INTVAL(i) = 283

I = 2.85
INT(i) = 284
INTVAL(i) = 284

I = 2.86
INT(i) = 285
INTVAL(i) = 285

I = 2.87
INT(i) = 286
INTVAL(i) = 286

Kann mir das irgend jemand erklären????
Bzw. brauche ich eine Lösung für mein Problem. Dachte durch das Umrechnen in Cent und "int-en" hätte ich meine Rechnenfehler gelöst, aber war leider noch nichts :-(

Bin für jeden Tip dankbar!

Gruß
lelleck

  1. Hi!

    Das 'Problem' hat jede Programmiersprache. Das liegt daran, wie Fliesskommazahlen intern verwaltet werden.

    Du hast das mit dem x * 100 schon richtig geloest. Aber du rechnest leider vorher noch.

    nicht 100 * (0.25 + 0.01) sondern bitte 100 * (0.25) + 1.

    Wird denn dein Centbetrag von 0.29 irgendwo errechnet?

    --
    Trau Dich!
     
    1. nicht 100 * (0.25 + 0.01) sondern bitte 100 * (0.25) + 1.

      OK, verstehe ich, war aber ja auch nur ein schnell dahin getipptes Beispiel zur veranschaulichung.

      Wird denn dein Centbetrag von 0.29 irgendwo errechnet?

      Nö, der Wert steht mit 0.29 in der MySQL Datenbank (Feld wie gesagt decimal 5,2). Über eine einfache sql-Abfrage ziehe ich mir diesen Wert aus der DB und weise einer Variable diesen Wert zu.
      PHP benutzt dann scheinbar Float als Datentyp, daher zunächst ja auch die gaaaanz vielen Nachkommastellen.

      Ich werde jetzt mal versuchen, die SQL Abfrage so abzuändern, das SQL selbst den Betrag mit 100 multipliziert, dann könnte ich ja theoretisch chancen haben, dass PHP den Wert als Integer erkennt.

      Gruss
      lelleck

      1. echo $begrüßung;

        PHP benutzt dann scheinbar Float als Datentyp, daher zunächst ja auch die gaaaanz vielen Nachkommastellen.

        Das Rätselraten um den Typ kann man sich sparen, wenn man eine Kontrollausgabe mit var_dump() macht.

        echo "$verabschiedung $name";

        1. Das Rätselraten um den Typ kann man sich sparen, wenn man eine Kontrollausgabe mit var_dump() macht.

          Guter Tip, Danke! PHP interpretiert meinen Betrag als 4-stelligen String, erst beim schreiben in die Session macht es ein Float daraus, weil ich dann die Anzahl mit dem String multipliziert habe.
          Echt doof!

          Probiere das nun morgen nochmal aus, dass ich den Betrag gleich von SQL mit 100 multiplizieren lasse.

          Gruss
          Sven

          1. Probiere das nun morgen nochmal aus, dass ich den Betrag gleich von SQL mit 100 multiplizieren lasse.

            Mal aus philosophischer Sicht: Bevor Du bei jeder Datenbankabfrage den Preis umrechnest, speichere ihn gleich in Pfennigen als Ganzzahl. Dann haste Ruhe vor jedem Ärger. Es gibt keinen Grund, DECIMAL in der Datenbank zu benutzen, um sich dann einen Integer zurückgeben zu lassen.
            (Wenn es noch nicht zu spät ist, die Datenbank zu ändern).

            Viele Grüße
            der Bademeister

            1. Hallo

              Mal aus philosophischer Sicht: Bevor Du bei jeder Datenbankabfrage den Preis umrechnest, speichere ihn gleich in Pfennigen als Ganzzahl.

              Auch wenn ich das auch befürwortete, muss ich doch sagen: Ganz schön altertümlich.

              Dann haste Ruhe vor jedem Ärger.

              Ich würde vermuten, er hätte doch Ärger, weil er die falsche Währungseinheit benutzt.
              *scnr*

              Tschö, Auge

              --
              Die deutschen Interessen werden am Liechtenstein verteidigt.
              Veranstaltungsdatenbank Vdb 0.2
  2. echo $begrüßung;

    [Rechenfehler beim Arbeiten mit Fließkommazahlen]
    Kann mir das irgend jemand erklären????

    Das Binärsystem ist genauso unzulänglich was die Darstellung von Brüchen angeht wie das Dezimalsystem. ⅓ (1/3) beispielsweise ist so ein Kandidat. Rechenfehler mit Fließkommazahlen sind eigentlich ein altbekanntes Phänomen.

    Bzw. brauche ich eine Lösung für mein Problem. Dachte durch das Umrechnen in Cent und "int-en" hätte ich meine Rechnenfehler gelöst, aber war leider noch nichts :-(

    PHP kennt anders als MySQL mit DECIMAL keinen Typ, der präzise mit Nachkommastellen umgehen kann. Lass dir gleich Integerwerte liefern oder lass die Berechnung von MySQL ausführen oder nimm ordentliche Preise. :-)

    Da der Wert als String zurückkommt und erst durch deine Rechnerei in einen Float-Typ gewandelt wird, könnte auch der Einsatz von BC Math oder GMP helfen, so sie auf deinem System vorhanden sind.

    echo "$verabschiedung $name";