round() - Rundungsfehler?
Michael H.
- php
hallo an Alle ebenso spät Arbeitenden,
Problem: Ich möchte das Ergebnis einer Rechnung kaufmännisch runden (sprich: wenn die Zahl die ich "wegschneide" eine 5 ist, wird aufgerundet).
Nun bekomme ich bei einem Beispiel einen Wert von 1.025 raus, den ich auf 2 Stellen runden will --> Ergebnis SOLLTE 1.03 sein, der round()-Befehl, der laut Doku kaufmännisch runde wirft mir 1.02 (schneidet den Wert also nur weg). Ist das ein bekannter Bug? Wenn ja, gibt dafür bereits fertige Funktionen, die das richtig machen? Keine Lust auf langes "Rad-neu-erfinden".
Merci,
Michael
P.S.: Schlaft nachher gut
Guten morgen :)
Also, ich habe das grad mal ausprobiert und bei mir ists genauso.
Ein Blick ins PHP-Manual hilft aber...
Eine Lösung dafür ist:
$add = 0.0000000001;
round(1.025 + $add, 2)
das gibt korrekt 1.03 aus.
Worans liegt kannst du da nachlesen.
http://www.php3.de/manual/de/function.round.php
Grüße
David
Hallo,
Problem: Ich möchte das Ergebnis einer Rechnung kaufmännisch runden (sprich: wenn die Zahl die ich "wegschneide" eine 5 ist, wird aufgerundet).
Nun bekomme ich bei einem Beispiel einen Wert von 1.025 raus, den ich auf 2 Stellen runden will --> Ergebnis SOLLTE 1.03 sein, der round()-Befehl, der laut Doku kaufmännisch runde wirft mir 1.02 (schneidet den Wert also nur weg).
Die round()-Funktion rundet mathematisch und schneidet nichts weg:
1.025 --> 1.02 (5 nach gerader Zahl abrunden)
1.075 --> 1.08 (5 nach ungerader Zahl aufrunden)
Ist das ein bekannter Bug? Wenn ja, gibt dafür bereits fertige Funktionen, die das richtig machen?
print round(1.025*100+0.5)/100; // --> 1.03
MfG, Thomas
Hallo Thomas,
Die round()-Funktion rundet mathematisch und schneidet nichts weg:
1.025 --> 1.02 (5 nach gerader Zahl abrunden)
1.075 --> 1.08 (5 nach ungerader Zahl aufrunden)
Das war vor 25 Jahren modern und gilt nicht mehr. Die Rundungsregeln sind in der DIN 1333 festgelegt. Die gilt seit 1992, die neuen Rundungsregeln sollte aber schon länger gelten, denn das änderte sich schon während meiner Schulzeit (ich tippe auf 1975-1980, da gab es eine umfassende Anpassung der ISO-Normen)
Also steht rechts neben der Rundungszahl ein Zahl die kleiner als 5 ist, wird abgerundet, sonst aufgerundet.
Das PHP richtig rundet zeigt das Beispiel:
echo round(1.125,2) ==> 1.13
Viele Grüße
Antje
Hallo,
Das war vor 25 Jahren modern und gilt nicht mehr.
OK, das war zu meiner Schulzeit und meiner "Kleinen Enzyklopaedie Mathematik" von 1983 vertraue ich noch heute ;-).
Die Rundungsregeln sind in der DIN 1333 festgelegt.
OK danke, da gehe ich doch naechste Woche gleich mal in unsere DIN-Auslegestelle.
Also steht rechts neben der Rundungszahl ein Zahl die kleiner als 5 ist, wird abgerundet, sonst aufgerundet.
Mich erstaunt nur, dass mir diese Aussage in diversen Rundungs-Threads ueber einen Zeitraum von 10 Jahren offenbar nie begegnet ist.
MfG, Thomas
Hallo,
Das PHP richtig rundet zeigt das Beispiel:
echo round(1.125,2) ==> 1.13
Aber hier offenbar doch nicht, oder?
echo round(1.025,2) ==> 1.02
Insofern sollte man mit
$x=1.025;
echo round($x*100+0.5)/100; ==> 1.03
besser fahren und so habe ich es auch immer gehalten, vor allem wenn keine expliziten Rundungsfunktionen vorhanden waren, etwa in Visual Basic < 6.
MfG, Thomas
Hallo!
Aber hier offenbar doch nicht, oder?
echo round(1.025,2) ==> 1.02
Vielleicht hilft es auch hier das Manual auf englisch(http://php3.de/manual/en/function.round.php) zu lesen (da aktueller), denn da steht noch eine kleine Warnung:
Caution
PHP doesn't handle strings like "12,300.2" correctly by default. See converting from strings.
=> http://php3.de/manual/en/language.types.string.php#language.types.string.conversion
Vielleicht hilft es ja!
Grüße
Andreas
Hallo,
Vielleicht hilft es auch hier das Manual auf englisch(http://php3.de/manual/en/function.round.php) zu lesen (da aktueller), denn da steht noch eine kleine Warnung:
Caution
PHP doesn't handle strings like "12,300.2" correctly by default. See converting from strings.
Mir ging es ja nicht um Strings, sondern die Dezimalzahl 1.025 und daraus ermitteln die PHP-Funktionen round() und number_format() 1.02. Die JavaScript-Methode toFixed() ergibt 1.03, ebenso wie die von mir seit > 15 Jahren verwendete Technik
x=1.025;
y=GanzeZahl(x*100+0.5)/100;
Gerade wegen dem Ergebnis 1.02 habe ich mich auf die offenbar veraltete Rundungsregel bezogen, die ich in der Praxis (z. B. bei naturwissenschaftlichen Studien) auch nicht einsetzte, sondern die oben genannte.
Ich habe auch mal alte Artikel nachgeschlagen und da habe ich noch von der VB6-Funktion Round() berichtet, die 0.125 zu 0.12 rundete. Da ich diese Version nicht hier habe, kann ich das nicht mit anderen Werten reproduzieren.
Insofern stellt sich die Frage, ob man also diesen Rundungsfunktionen traut oder sich lieber auf die bewaehrten Praktiken verlaesst.
MfG, Thomas
Hi!
Insofern stellt sich die Frage, ob man also diesen Rundungsfunktionen traut oder sich lieber auf die bewaehrten Praktiken verlaesst.
Die Fraqge ist, ob man float-Daten traut oder nicht. Habe mal bei bugs.php.net nach round gesucht, da hatten einige schon an einen PHP-bug gedacht(z.B. http://bugs.php.net/bug.php?id=13241), aber das Problem ist wohl das Floor-Daten nicht 100% genau sind, wenn ich das richtig verstanden habe, das steht auch da:
http://php3.de/manual/de/language.types.float.php
"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."
Also kann man sich wirklich nicht auf round & co. verlassen, ich hoffe das hat keine Auswirkungen auf laufende Scripte von mir...
Grüße
Andreas
Hallo,
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 ist ja bekannt, es kommt aber auch noch auf die konkrete Implementierung an.
Bei meinen Beispielen ergaben sich Unterschiede zwischen PHP und JavaScript, also ist offenbar der Algorithmus von round($x,$y) nicht identisch zu x.toFixed(y). Die interne Genauigkeit der beiden Sprachen duerfte vergleichbar sein.
Also kann man sich wirklich nicht auf round & co. verlassen, ich hoffe das hat keine Auswirkungen auf laufende Scripte von mir...
Ich habe mir mal noch die round()-Funktion von MySQL angesehen. Diese rundet bis zur Endziffer 5 grundsaetzlich ab:
select round(1.95,1) --> 1.9
select round(1.025,2) --> 1.02
select round(1.075,2) --> 1.07
select round(123.4555,3) --> 123.455
Auch hier waeren wieder Workarounds faellig ...
MfG, Thomas
Hi
Wenn ja, gibt dafür bereits fertige Funktionen, die das richtig machen? Keine Lust auf langes "Rad-neu-erfinden".
ja,
<schnipp>
function roundoff($v, $d) {
$r = pow(10, $d);
$v *= $r;
if($v - floor($v) >= 0.5)
{
return (ceil($v)/$r);
}
else
{
return (floor($v)/$r);
}
}
</schnapp>
wenn es natürlich wirklich die Probleme mit floor gibt ist dass wohl nicht ganz sicher!? Habe allerdings noch nie Probleme gehabt!
ciao
romy