Rechnen mit php

Rechnen mit php

am 08.04.2008 07:01:08 von Beat Schilliger

Hallo miteinander,

ich verstehe momentan die (php)-Welt nicht mehr. Ich versuche einen
dezimalen Geldbetrag in Franken und Rappen (entspricht Euro und Cent)
aufzuteilen. Doch erhalte ich jeweils einen falschen Rappenbetrag, sobald
dieser die Höhe von 87 übersteigt!

Dazu habe ich folgendes Skript erstellt:

$total = 28.90;
$franken = floor($total);
$rappen = sprintf("%02d",($total - $franken) * 100);
echo "Total: $total
";
echo "Franken: $franken
";
echo "Rappen: $rappen";
?>

Als Ausgabe erhalte ich Folgendes:

Total: 28.9
Franken: 28
Rappen: 89

Warum werden ab Rest .88 vom Totalbetrag jeweils 1 weniger gerechnet?

28.87 wird zu 28 und 87
28.88 wird zu 28 und 87

Hat dafür jemand eine pausible Erklärung, bzw. hat mir einen Hinweis, wie
ich es hinkriege, dass ich die beiden gewünschten Werte in der richtigen
Höhe hinbekomme?

Besten Dank und einen schönen Gruss aus der Schweiz

Beat

Re: Rechnen mit php

am 08.04.2008 07:11:34 von Niels Braczek

Beat Schilliger schrieb:

> Warum werden ab Rest .88 vom Totalbetrag jeweils 1 weniger gerechnet?
>=20
> 28.87 wird zu 28 und 87
> 28.88 wird zu 28 und 87
>=20
> Hat dafür jemand eine pausible Erklärung, bzw. hat mir einen Hinwei=
s, wie
> ich es hinkriege, dass ich die beiden gewünschten Werte in der richti=
gen
> Höhe hinbekomme?

Das passiert nicht nur in PHP, sondern mit so gut wie jeder
programmiersprache. Computer rechnen mit Binärzahlen. Dezimalstellen
sind idR nicht endlich darstellbar, was zu Rundungsfehlern (wie
beobachtet) führt, wenn man keine Vorkehrungen trifft.

> $rappen =3D sprintf("%02d",($total - $franken) * 100);

sprintf() ist eine Zeichenkettenfunktion und somit für Rechenoperatione=
n
völlig unbrauchbar.

$rappen =3D round( ( $total - $franken ) * 100 );
printf( '%02d', $rappen );

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Rechnen mit php

am 08.04.2008 09:41:43 von Frank Arthur

> Beat Schilliger schrieb:
>
>> Warum werden ab Rest .88 vom Totalbetrag jeweils 1 weniger gerechnet?
>>
>> 28.87 wird zu 28 und 87
>> 28.88 wird zu 28 und 87
>>
>> $rappen = sprintf("%02d",($total - $franken) * 100);

Niels Braczek schrieb:
>
> sprintf() ist eine Zeichenkettenfunktion und somit für Rechenoperationen
> völlig unbrauchbar.
>
> $rappen = round( ( $total - $franken ) * 100 ); printf( '%02d', $rappen
> );

Jupp.
Oder weniger Mathematisch:
$parts = explode ('.', 28.88);
echo 'Franken: ',$parts[0];
echo '
Rappen: ',$parts[1];

Re: Rechnen mit php

am 08.04.2008 10:00:40 von Beat Schilliger

Hallo Niels,

Am Tue, 08 Apr 2008 07:11:34 +0200 schrieb Niels Braczek:
>> Hat dafür jemand eine pausible Erklärung, bzw. hat mir einen Hinweis, wie
>> ich es hinkriege, dass ich die beiden gewünschten Werte in der richtigen
>> Höhe hinbekomme?
>
> Das passiert nicht nur in PHP, sondern mit so gut wie jeder
> programmiersprache. Computer rechnen mit Binärzahlen. Dezimalstellen
> sind idR nicht endlich darstellbar, was zu Rundungsfehlern (wie
> beobachtet) führt, wenn man keine Vorkehrungen trifft.
>
>> $rappen = sprintf("%02d",($total - $franken) * 100);
>
> sprintf() ist eine Zeichenkettenfunktion und somit für Rechenoperationen
> völlig unbrauchbar.
>
> $rappen = round( ( $total - $franken ) * 100 );
> printf( '%02d', $rappen );

besten Dank für deine Erklärung.

Da ich den Rappenbetrag einer Variablen zuordnen muss (verwende diese um
den Rappenbetrag in einem pdf Dokument auszugeben), habe ich im Anschluss
der von dir mitgeteilten Lösung mit dem round trotzdem sprintf()
verwendet.

$rappen = round( ( $total - $franken ) * 100 );
$rappen = sprintf( '%02d', $rappen );

Besten Dank und einen schönen Gruss

Beat

Re: Rechnen mit php

am 08.04.2008 10:07:36 von Heiko Richler

Frank Arthur wrote:
> Niels Braczek schrieb:
>> sprintf() ist eine Zeichenkettenfunktion und somit für Rechenoperationen
>> völlig unbrauchbar.
>>
>> $rappen = round( ( $total - $franken ) * 100 ); printf( '%02d', $rappen
>> );
>
> Jupp.
> Oder weniger Mathematisch:
> $parts = explode ('.', 28.88);
> echo 'Franken: ',$parts[0];
> echo '
Rappen: ',$parts[1];

Das funktioniert hier weil 28.88 eine Konstante Zahl, deren
Interpretation als String vermutlich auch mit nur zwei Nachkommastellen
auskommt.

Sicherer wäre es dann schon so:
$mwst = 28.88 * 0.065;
$parts = explode ('.', round($mwst, 2));

Der Umweg über die Darstellung sieht nett aus, aber ich würde die
Variante vom Niels bevorzugen. Da steht einfach was gemeint ist.

Heiko
--
http://portal.richler.de/ Namensportal zu Richler
http://www.richler.de/ Heiko Richler: Computer - Know How!
http://www.richler.info/ private Homepage

Re: Rechnen mit php

am 08.04.2008 10:50:43 von Niels Braczek

Beat Schilliger schrieb:

> Da ich den Rappenbetrag einer Variablen zuordnen muss (verwende diese u=
m
> den Rappenbetrag in einem pdf Dokument auszugeben), habe ich im Anschlu=
ss
> der von dir mitgeteilten Lösung mit dem round trotzdem sprintf()
> verwendet.
>=20
> $rappen =3D round( ( $total - $franken ) * 100 );
> $rappen =3D sprintf( '%02d', $rappen );

sprintf() würde ich erst bei der Zuweisung an das PDF-Dokument verwende=
n
und eben *nicht* der Variablen $rappen zuweisen. Ich halte nichts davon,
den Datentyp ohne Not zu wechseln.

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Rechnen mit php

am 08.04.2008 12:55:04 von Beat Schilliger

Am Tue, 08 Apr 2008 10:50:43 +0200 schrieb Niels Braczek:
Hallo Niels,


> sprintf() würde ich erst bei der Zuweisung an das PDF-Dokument verwenden
> und eben *nicht* der Variablen $rappen zuweisen. Ich halte nichts davon,
> den Datentyp ohne Not zu wechseln.

besten Dank für deine Antwort. Werde dies entsprechend deinen Angaben ohne
zusätzliche Variable umsetzen.

Einen schönen Gruss

Beat

Re: Rechnen mit php

am 08.04.2008 18:10:56 von Claus Reibenstein

Beat Schilliger schrieb:

> $total = 28.90;

$total = 2890;

Wenn man mit Geldbeträgen rechnet, sollte man _immer_ mit der kleinsten
Einheit rechnen. Dann hat man es nur mit ganzen Zahlen zu tun und
vermeidet so Rundungsfehler, die u.a. zu dem von Dir beobachteten
Phänomen führen können.

Gruß. Claus

Re: Rechnen mit php

am 08.04.2008 23:14:03 von Benjamin Zikarsky

Claus Reibenstein schrieb:

> Wenn man mit Geldbeträgen rechnet, sollte man _immer_ mit der kleinsten
> Einheit rechnen.

Solange du keine Geldbeträge über 21.474.836,47 $waehrung auf 32bit
Systemen hast.
Ich wünschte, ich könnte mein Konto dann für einen Unit-Test hergeben. ;)

SCNR
Benjamin

Re: Rechnen mit php

am 08.04.2008 23:34:28 von Niels Braczek

Claus Reibenstein schrieb:

> Wenn man mit Geldbeträgen rechnet, sollte man _immer_ mit der kleinst=
en
> Einheit rechnen. Dann hat man es nur mit ganzen Zahlen zu tun und
> vermeidet so Rundungsfehler, die u.a. zu dem von Dir beobachteten
> Phänomen führen können.

Auch wenn eine Währung wie der Franken oder der Euro offiziell nur zwei=

Nachkommastellen hat, ist es in vielen Branchen üblich, mit deutlich
mehr Stellen zu arbeiten. Das Rundungsproblem wirst du so also nicht los.=


Die korrekte Vorgehensweise ist ein explizites Runden an geeigneten
Stellen im Programmablauf und die *vollständige* Trennung von Berechnun=
g
und Aufbereitung für die Ausgabe.

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Rechnen mit php

am 09.04.2008 18:02:07 von Claus Reibenstein

Benjamin Zikarsky schrieb:

> Claus Reibenstein schrieb:
>
>> Wenn man mit Geldbeträgen rechnet, sollte man _immer_ mit der kleinsten
>> Einheit rechnen.
>
> Solange du keine Geldbeträge über 21.474.836,47 $waehrung auf 32bit
> Systemen hast.

Für solche Fälle gibt es Bibliotheken, die mit beliebig langen
Ganzzahlen arbeiten.

Gruß. Claus

Re: Rechnen mit php

am 09.04.2008 18:05:00 von Claus Reibenstein

Niels Braczek schrieb:

> Claus Reibenstein schrieb:
>
>> Wenn man mit Geldbeträgen rechnet, sollte man _immer_ mit der kleinsten
>> Einheit rechnen. Dann hat man es nur mit ganzen Zahlen zu tun und
>> vermeidet so Rundungsfehler, die u.a. zu dem von Dir beobachteten
>> Phänomen führen können.
>
> Auch wenn eine Währung wie der Franken oder der Euro offiziell nur zwei
> Nachkommastellen hat, ist es in vielen Branchen üblich, mit deutlich
> mehr Stellen zu arbeiten. Das Rundungsproblem wirst du so also nicht los.

Das Rundungsproblem, das sich aus der Verwendung von Gleitkommawerten
ergibt, werde ich damit sehr wohl los.

Was Du hier ansprichst, ist eine ganz andere Baustelle. Aber auch dort
wird nur mit ganzen Zahlen gearbeitet.

> Die korrekte Vorgehensweise ist ein explizites Runden an geeigneten
> Stellen im Programmablauf und die *vollständige* Trennung von Berechnung
> und Aufbereitung für die Ausgabe.

Die korrekte Vorgehensweise ist die Verwendung geeigneter Datentypen.

Gruß. Claus