rounding problems

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

hessodreamy
Forum Commoner
Posts: 58
Joined: Wed Apr 20, 2005 8:11 am

rounding problems

Post by hessodreamy »

I've run into trouble with rounding the results of calculations. I presume its to do with floating-point precision, but can't seem to get around it.

take the following:

Code: Select all

$number = 227*0.175;

echo "number = $number<BR>";
echo "cast to float : ".(float) $number."<BR>";
echo "var_dump : ".var_dump($number)."<BR>";
echo "using round : ". round($number,2)."<BR>";
echo "using number format : ". number_format($number,2,".","")."<BR>";
echo "multiply by 100 and round to int: ".round($number*100)."<BR>";
echo "multiply by 100 and cast to int : ".(int)($number*100)."<BR>";
this gives me :

Code: Select all

number = 39.725
cast to float : 39.725
float(39.725) var_dump : 
using round : 39.72
using number format : 39.72
multiply by 100 and round to int: 3972
multiply by 100 and cast to int : 3972
As you can see the number (39.725) is rounded down to 39.72 in each case, instead of up. Is this due to floating points?
If I say $number = 39.725 then it rounds upwards to 39.73 (except for casting to int).
How do I get around this?

I'm trying to make sure all the figures work out as they do in our acconting software. They probably store pence rather than pounds but I'm not sure this would make any difference in this case. If I multiply by a float (0.175) then I'll end up with a float again, won't I.

HELP ME!!!
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I'd suggest using bc (arbitrary length decimal math functions) for anything where the mantissa is critical (like accounting)
hessodreamy
Forum Commoner
Posts: 58
Joined: Wed Apr 20, 2005 8:11 am

Post by hessodreamy »

OK. That seemed like a good plan. But, with the maths i've already got, turning calculations into function calls makes the code pretty hard to follow. Unless I layout the code differently, & a little more verbose.

Someone suggested casting the calculation result to a string. Lo and behold - it worked. Is there significant detriment to this method?

The use is in a shopping cart application. I'm calculating vat on each product line. The important thing is everything works out the same as our accounting seftware works out. ie the calculation and rounding of vat, and the subsequent totalling of net/gross figures.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

use the bc functions. True, they are less intuitive to read than basic math, but since PHP lacks operator overrides/overloads, there's little than can be done about it. Maybe create a math object and use that instead then you can switch which functions or basic math is used internally while maintaining the same usage in the program itself.
hessodreamy
Forum Commoner
Posts: 58
Joined: Wed Apr 20, 2005 8:11 am

Post by hessodreamy »

I found the bc functions no better. Maybe I was using them wrong but there were some calculations such as 93*0.175 =16.275 which still rounds down to 16.27
I found this function much better.
http://www.php-forum.com/p/viewtopic.ph ... 03#4366603
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Someone suggested casting the calculation result to a string. Lo and behold - it worked. Is there significant detriment to this method?
I think in general PHP is worse than other languages at floating point arithmatic. Its very disappointing your problem even occurred and somewhat shocking that this was the solution.
use the bc functions. True, they are less intuitive to read than basic math, but since PHP lacks operator overrides/overloads, there's little than can be done about it.
are there plans to implement operating overloading? it would be really nice.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Unfortunately there are many many ideas about rounding. The implementors of PHP had to choose one. They chose to round 0.5 down instead of up. That is a fairly common rounding method.
http://en.wikipedia.org/wiki/Rounding

As for operator overloading, it's not in PHP 6, so not for a while, if ever.
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Post by Oren »

Since when? PHP rounds up... http://us2.php.net/manual/en/function.round.php
And it's not a common method. Follow the link you posted yourself feyd... It is written there (under 'Common method of rounding'):
Example: 3.046 rounded to hundredths is 3.05 (because the next digit [6] is 5 or more).
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Thanks for proving my point Oren.
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Post by Oren »

What do you mean?
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

6 > 5, of course it'll round up. round(3.045, 2) = 3.04 in PHP's standard build.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Post by pickle »

Holy crap does that every make things difficult when doing any sort of billing with PHP. Thanks for the heads up ~feyd- I didn't realize this was the case.

/me goes to try and fix the billing program I've been using for 2 years.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
someberry
Forum Contributor
Posts: 172
Joined: Mon Apr 11, 2005 5:16 am

Post by someberry »

Hrrmmm, don't know about you, but whenever I use the round function, point 5 is rounded up:

Code: Select all

<?PHP
echo(round(39.724, 2) . '<br />'); // Output: 39.72
echo(round(39.725, 2) . '<br />'); // Output: 39.73
echo(round(39.726, 2) . '<br />'); // Output: 39.73
?>
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Post by pickle »

Good point. ~feyd, 3.045 rounds to 3.05 on my box. As far as I know I've got a pretty standard build - any idea what might change that outcome?
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Post by Oren »

Here is (again) the quote from Wiki:
Example: 3.046 rounded to hundredths is 3.05 (because the next digit [6] is 5 or more).
This time the important part is bold. Also read the above 2 posts^^^
Post Reply