PHP - bedrag uit xml vermenigvuldigen - verkeerde uitkomst

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • efan
  • Registratie: Januari 2001
  • Niet online
Mijn vraag

Ik heb even als test een XML bestand met de volgende inhoud:
XML:
1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<product>
  <price currency="EUR">172.79</price>
</product>


vervolgens een php file waarin ik de prijs uit het XML bestand wil vermenigvuldigen (in dit geval met 1.21 als BTW). Het bedrag komt goed over uit de XML, maar zodra je het vermenigvuldigd, dan klopt de uitkomst niet. Hij geeft aan 208.12 , terwijl het 209,08 zou moeten zijn.

PHP:
1
2
3
4
5
6
7
8
<?php
    $xml = simplexml_load_file('list.xml');
    echo $xml->price,"<br>";
    $prijs = $xml->price;
    $btw = 1.21;
    echo "prijs excl. btw (172.79): ",$prijs,"<br>";
    echo "prijs incl. btw (moet zijn 209,0759): ",$prijs,"x",$btw," = ",($prijs*$btw);
?>


Dit geeft als uitkomst:
172.79
prijs excl. btw (172.79): 172.79
prijs incl. btw (moet zijn 209,0759): 172.79x1.21 = 208.12


Relevante software en hardware die ik gebruik

XML, PHP 5.6.31 en wampserver 64 3.1.0 ;)

Iemand enig idee wat ik hier over het hoofd zie?
Als ik zelf
PHP:
1
172.79*$btw
doe, dan krijg ik wel het juiste bedrag.

Beste antwoord (via efan op 13-07-2018 19:36)


  • TheBorg
  • Registratie: November 2002
  • Laatst online: 05-10 18:02

TheBorg

Resistance is futile.

Jop je de 79 cent weg en je vermenigvuldigd met 1,21 dan kom ik op 208,12

De centen zijn dus kwijt geraakt. Misschien is een "number" een "integer" geworden.

Misschien werkt een cast.

code:
1
$prijs = (double) $xml->price;


Of een conversion:

code:
1
$prijs = floatval($xml->price);

[ Voor 13% gewijzigd door TheBorg op 13-07-2018 19:34 ]

Alle reacties


Acties:
  • Beste antwoord
  • +2 Henk 'm!

  • TheBorg
  • Registratie: November 2002
  • Laatst online: 05-10 18:02

TheBorg

Resistance is futile.

Jop je de 79 cent weg en je vermenigvuldigd met 1,21 dan kom ik op 208,12

De centen zijn dus kwijt geraakt. Misschien is een "number" een "integer" geworden.

Misschien werkt een cast.

code:
1
$prijs = (double) $xml->price;


Of een conversion:

code:
1
$prijs = floatval($xml->price);

[ Voor 13% gewijzigd door TheBorg op 13-07-2018 19:34 ]


Acties:
  • 0 Henk 'm!

  • efan
  • Registratie: Januari 2001
  • Niet online
TheBorg schreef op vrijdag 13 juli 2018 @ 19:30:
Jop je de 79 cent weg en je vermenigvuldigd met 1,21 dan kom ik op 208,12

De centen zijn dus kwijt geraakt. Misschien is een "number" een "integer" geworden.

Misschien werkt een cast.

code:
1
$prijs = (double) $xml->price;


Of een conversion:

code:
1
$prijs = floatval($xml->price);
Thanks, dat doet het _/-\o_ Dacht dat PHP het zelf wel zou zien als double/float :X

Acties:
  • 0 Henk 'm!

  • jixxed
  • Registratie: April 2006
  • Laatst online: 03-07 17:40
Je moet zeker met valuta rekening houden met afrondingsfouten. Door doubles te vermenigvuldigen kan je zomaar een cent afwijking krijgen.

http://php.net/manual/en/language.types.float.php

er zijn functies beschikbaar om veilig doubles te vermenigvuldigen.

Acties:
  • 0 Henk 'm!

  • efan
  • Registratie: Januari 2001
  • Niet online
jixxed schreef op vrijdag 13 juli 2018 @ 19:37:
Je moet zeker met valuta rekening houden met afrondingsfouten. Door doubles te vermenigvuldigen kan je zomaar een cent afwijking krijgen.

http://php.net/manual/en/language.types.float.php

er zijn functies beschikbaar om veilig doubles te vermenigvuldigen.
OK, bedankt voor de tip. Als ik even wat lees op bijv SO zie ik bijv:
For pure rounding/display purposes, you're safe as long as the absolute floating-point representation error is less than $0.005 (so that rounding to the nearest cent is correct).

With IEEE 754 single-precision, you're safe up to $131,072.00. ($131,072.01 is represented as 131072.015625, which incorrectly rounds up.)

Double precision (which PHP's float uses) doesn't fail until $70,368,744,177,664.01 (which also has .015625 for the cents). You have nothing to worry about.
Dus zolang ik niet met zulke grote bedragen reken, dan lijkt het me dat ik geen aanpassing hoef te doen?

Acties:
  • +2 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
ido schreef op vrijdag 13 juli 2018 @ 20:07:
Dus zolang ik niet met zulke grote bedragen reken, dan lijkt het me dat ik geen aanpassing hoef te doen?
Hangt er van af. De belastingdienst wil de BTW berekening over de gehele bestelling.

Nu komt het:
Een product van 0.99 excl. BTW = 1.1979 = 1.20

Ik koop bij jou 3 producten.
3x 1.1979 incl. BTW = 3.5937 = 0.6237 = 0.62 BTW

Ik kom 3x in je winkel en koop één product.
1x 1.1979 incl. BTW = 0.2079 BTW = 0.21 x 3 = 0.63 BTW

Jij hebt de prijzen incl. BTW in je database als 1.20
3x 1.20 incl. BTW = 3.60 = 0.62479338843 = 0.62 BTW
1x 1.20 incl. BTW = 0.20826446281 = 0.21 x 3 = 0.63 BTW

Door de afronding verschillen (zie aantal cijfers achter komma) krijg je bij float verschil in de centen.

Zolang je de BTW over de gehele bestelling (alle soorten producten bij elkaar op de rekening) achteraf berekent zit je redelijk safe.
Dus niet per product berekenen bij verkoop ;)

Maak je niet druk, dat doet de compressor maar


Acties:
  • +1 Henk 'm!

  • MartenvanUrk
  • Registratie: Oktober 2012
  • Laatst online: 16-04 08:11
Ik zou toch ook aanraden om de BC math functies te gebruiken. Vooral bij dit soort berekeningen. Verder opslaan met 5 achter de komma. Dan zit je vaak wel goed.

Acties:
  • 0 Henk 'm!

Verwijderd

$xml->price is geen string maar een SimpleXMLElement instance. Automatische type conversie van objecten naar string / int / float is altijd tricky. Zeker als het geen PHP class met __toString() is (SimpleXML is een C-library, zie dat daar een cast functie in zit die vermoedelijk niet meekrijgt dat het om een 'double'-type gaat en terugvalt op conversie naar een integer)

Als je $price = (string) $xml->price gebruikt werkt het overigens ook. Best is waarden altijd te casten.

Acties:
  • +1 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 06-10 17:29

Matis

Rubber Rocket

Let me wel op localization? De ene keer schrijf je getallen met een komma als scheiding tussen getal en decimalen. De andere keer gebruik je een punt.
Zeker als je van string naar float gaat, kan dat wel eens fout gaan.

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

MartenvanUrk schreef op woensdag 18 juli 2018 @ 12:35:
Ik zou toch ook aanraden om de BC math functies te gebruiken. Vooral bij dit soort berekeningen. Verder opslaan met 5 achter de komma. Dan zit je vaak wel goed.
Dit ja. Nooit floats gebruiken voor berekeningen met geld i.v.m. afrondingsfouten. Hoe vaak ik dat wel niet fout zie gaan bij webshops 8)7
Pagina: 1