round() - Fehler ?

Alles, was PHP betrifft, kann hier besprochen werden.

round() - Fehler ?

Postby Oneside » 13. October 2004 08:36

Hallo Members,

wenn ich folgenden Befehl ausführe:
round (2.135,2);
dann müsste ja 2.14 rauskommen.
Ich bekomme aber 2.13 raus. Woran kann das liegen?
Oneside
 
Posts: 19
Joined: 13. October 2004 08:30

Postby Hanspeter Thöni » 13. October 2004 09:24

Auszug aus der PHP Dokumentation -> Datentypen -> Fliesskommazahlen

Fließkomma Präzision
Es ist ziemlich normal, dass einfache Dezimalzahlen wie 0.1 oder 0.7 nicht in ihre internen binären Entsprechungen konvertiert werden können, ohne einen kleinen Teil ihrer Genauigkeit zu verlieren. Das kann zu verwirrenden Ergebnissen führen. So wird floor((0.1 + 0.7) * 10) normalerweise 7 statt des erwarteten Wertes 8 zurück geben (als Ergebnis der internen Entsprechung von 7.9999999999....

Das gründet sich auf die Tatsache, dass es unmöglich ist, manche Dezimal-Zahlen durch eine endliche Anzahl an Nachkomma-Stellen darzustellen. Dem Wert 1/3 entspricht z.B. der interne Wert von 0.3333333. . ..

Deshalb sollten Sie nie den Ergebnissen von Fließkomma-Operationen bis auf die letzte Nachkomma-Stelle trauen und nie solche auf Gleichheit prüfen. Benötigen Sie wirklich eine größere Genauigkeit, sollten sie die mathematischen Funktionen beliebiger Genauigkeit oder die Gmp Funktionen benutzen.
Gruss Hanspeter
User avatar
Hanspeter Thöni
 
Posts: 233
Joined: 17. August 2004 10:29
Location: Schweiz

Postby Oneside » 13. October 2004 09:42

Mit welchem GMP Befehl kann ich eine Zahl auf zwei Nachkommastellen runden?
Oneside
 
Posts: 19
Joined: 13. October 2004 08:30

Postby Wiedmann » 13. October 2004 14:37

Hi Oneside,

hier mal ein Zitat das dir eine Erklärung liefert und auch eine mögliche Lösung:
This is expected behaviour. When rounding on exact halves we round down on evens and up on odds. If you want to always force it in one direction on a .5 (or .135 in your case) add or substract a tiny fuzz factor.
Wiedmann
AF Moderator
 
Posts: 17102
Joined: 01. February 2004 12:38
Location: Stuttgart / Germany

Postby Oneside » 13. October 2004 15:37

Vielen Dank.

Hier mal eine Lösung, die anderen helfen könnte, die das gleiche Prob. haben:

Code: Select all

<?php

function myRound($number, $fuzz = 0.00000000001){
  return sprintf("%.2f", (($number>=0) ? ($number+$fuzz) : ($number-$fuzz)));
}

echo myRound(1.345);

?>

Oneside
 
Posts: 19
Joined: 13. October 2004 08:30

Postby boppy » 14. October 2004 09:50

Ich würde deine version jedoch noch ein wenig verfeinern:

Code: Select all
function myRound($double,$decimals=2){
    $tmp = explode(".",doubleval($double));
    if(count($tmp)==2 && strlen($tmp[1]) > $decimals){
        $fuzz = pow(10,-11);
        return sprintf("%.${decimals}f", (($double>=0) ? ($double+$fuzz) : ($double-$fuzz)));
    } else {
        return doubleval($double);
    }
}


ich verlier auch mal kurz 2 oder 3 (hoch x *g*) Worte dazu:

eingegeben wird: myRound(zahl,dezimalstellen).
Das Script zerlegt die in $double übergebene Zahl in durch einen PUNKT getrennte Teile, nachdem es eine Formatumwandlung in ein DOUBLE hinter sich hat (nicht wirlich nötig, aber ein bissel schnick-schnack kann nicht schaden.).

Dann wird geprüft, ob es wirklich 2 Teile sind, die zurück kommen (man könnte ja auch versehendlich 2.15.2 eingeben...) und guckt, ob denn der Bereich nach dem Komma auch länger ist, als die Stellenzahl, die wir wirklich sehen wollen (prüft er dies nicht, kommen abendteuerliche sachen raus, könnt ihr ja mal testen ;)). Dann legt das Script den $fuzz fest. Da ich nicht so der Fan der vielen nullen bin, lasse ich das durch PHP erstellen. Es bleibt alelrdings das gleich, was auch deinen original zu finden ist.
Naja, der rest ist ja ziemlich easy. Ich setze fest, dass er die in $decimals festgelgte Zahl als nachkommastellen darstellen soll und lasste, falls der obere ausdruck FALSE zurück gibt, das in $double übergebene nur in ein double umwandeln und gebe es zurück.

Beispiele:

myRound(1.345,2) = 1.35
myRound("1.345",2) = 1.35
ist ja klar... mit einem übergebenen String arbeitet er auch...


myRound("1.345 €",2) = 1.35
myRound("1.345 ist nicht 4",9) = 1.345
Alles, was nach dem wirklichen double kommt, wir geschibbelt. Auch wenn die 4 eigentlich eine zahl ist, fällt sie weg, weil sie nach einem text kommt.
...in diesem Sinne
yours boppy

Interpunktion und Orthographie dieses Beitrags sind frei erfunden.
Eine Übereinstimmung mit aktuellen oder ehemaligen Regeln wäre rein zufällig und ist nicht beabsichtigt.
User avatar
boppy
AF Moderator
 
Posts: 501
Joined: 27. December 2002 02:15
Location: W-E-City

Postby Wiedmann » 14. October 2004 14:15

Hier mal eine Lösung, die anderen helfen könnte, die das gleiche Prob. haben:

Eigentlich muss man gar nicht soviel Aufwand treiben... Es geht dir ja nur darum, dass bei einer 5 aufgerundet wird. Bei zwei Stellen nach dem Komma wäre das dann:
Code: Select all
echo round(1.345 + 0.0001, 2);


als Funktion dann z.B. so:
Code: Select all
<?php
function myround($val, $precision = 0) {
   $precisionadd = (int) $precision + 2;
   $precisionadd = 1 * "1E-$precisionadd";
   return round((float) $val + $precisionadd, (int) $precision);
}
echo myround(1.345, 2);
?>


Alle Lösungen hätten trozdem noch ein Problem mit z.B.: 1.3445
Wiedmann
AF Moderator
 
Posts: 17102
Joined: 01. February 2004 12:38
Location: Stuttgart / Germany

Postby boppy » 14. October 2004 17:05

Wiedmann wrote:Alle Lösungen hätten trozdem noch ein Problem mit z.B.: 1.3445
warum? also sowohl meine als auch die round(1.345 + 0.0001, 2);-Lösung runden bei 1,3445 auf 2 und auf 3 stellen vollkommen korrekt!
Oder bist du einer der fraktion, der gerne rundes rundet? der also bei

round(1,4444444444444444444444444444445,0)

gerne 2 heraus hätte? ;) - das wäre ja mathematisch gesehen ein desaster *g*
...in diesem Sinne
yours boppy

Interpunktion und Orthographie dieses Beitrags sind frei erfunden.
Eine Übereinstimmung mit aktuellen oder ehemaligen Regeln wäre rein zufällig und ist nicht beabsichtigt.
User avatar
boppy
AF Moderator
 
Posts: 501
Joined: 27. December 2002 02:15
Location: W-E-City


Return to PHP

Who is online

Users browsing this forum: No registered users and 13 guests