Possible Workaround for Inaccurate Floating Point Rounding? - php

I have created a PHP system that is having some problems with rounding. After looking further into this, I found that it goes back to the round function in PHP.
For instance...
Rounding 2047.615 to 2 decimal places gives 2047.62 (as expected)
Rounding 2048.615 to 2 decimal places gives 2048.61 (doesn't round up as expected)
I understand that the issue here most likely goes back to the inaccuracy of representing floating numbers in binary, but what is the most elegant way to take care of such issues?

It rounds as expected for me. Have you tried explicitly setting the mode parameter to round()?
round( 2048.615, 2, PHP_ROUND_HALF_UP); // however, PHP_ROUND_HALF_UP is the default
EDIT: It looks like round() was changed in PHP 5.3.0:
Changed round() to act more intuitively when rounding to a certain
precision and round very large and very small exponents correctly.
(Christian Seiler)2

2048.615 is actually 2048.6149999999998, so it will round down to 2048.61 no matter the rounding method used.

Most likely your particular round function is performing Banker's Rounding (or PHP_ROUND_HALF_EVEN).
If you want a different kind of rounding, use one of the other PHP rounding variants:
<?php
echo round(9.5, 0, PHP_ROUND_HALF_UP); // 10
echo round(9.5, 0, PHP_ROUND_HALF_DOWN); // 9
echo round(9.5, 0, PHP_ROUND_HALF_EVEN); // 10
echo round(9.5, 0, PHP_ROUND_HALF_ODD); // 9
echo round(8.5, 0, PHP_ROUND_HALF_UP); // 9
echo round(8.5, 0, PHP_ROUND_HALF_DOWN); // 8
echo round(8.5, 0, PHP_ROUND_HALF_EVEN); // 8
echo round(8.5, 0, PHP_ROUND_HALF_ODD); // 9
?>

Here's the best solution I've come up with. In the following snippet I separate out the integer and decimal parts of the number and then do my rounding just on the decimal part (which gives me more bits available to work on the decimal accuracy). Does anybody see any problems with this solution?
function my_round($number, $decimals)
return floor($number) + round(round($number - floor($number), $decimals + 4), $decimals);
}
The "+ 4" is somewhat arbitrary (about half of the trailing digits of detail), but is attempting to be accurate enough without being so accurate as to run into the .499999999 issue causing this whole thing.

Related

PHP round() bug with round(908.5449, 2, PHP_ROUND_HALF_UP) = 908.54?

I am currently investing a rounding problem in PHP.
I've tested it here https://3v4l.org/NpRPp and it seems to be the same, since ever.
But is this correct behaviour? Because i expected the following.
echo round(908.5449, 2, PHP_ROUND_HALF_UP); // should be 908.55, but returns actually 908.54
Can someone tell if this behaviour is intended, and if so, how to solve it properly for every possible floating point number?
Edit: Thanks to the answers, this behaviour is correct but i expected the wrong result. My original number in the program was 908.55 which somewhere in the chain of the program became 908.5449999999994 due to storage in DB and floating point precision issues. So i wanted it to be 908.55 again, which does not work with regular use of round().
This is working correctly. The output of
echo round(908.5449, 2, PHP_ROUND_HALF_UP);
Should be 908.54. You're rounding to two decimals, so the remainder, that is rounded off is 0.0049. This is less than 0.005, which would be half, so it is rounded down.

PHP sprintf('%d', floatval('16.40') * 100) outputs 1639?

The sprinf behaviour below is very strange.
// The code below outputs 1639
$value = floatval('16.40') * 100;
echo sprintf('%d', $value);
But when I use %f it outputs 1640.000000
I dumped the $value, it's definitely float(1640).
Does anyone knows what happened when I use %d? Is this a PHP bug? I've tested in PHP7.
The variable $value contains a floating point number. Floating point numbers are not exact, they are approximations.1
The value of $value is not 1640 but 1639.99999999999977262632. When you use %f to print it, printf() rounds it to 6 decimal places and the rounded value is 1640.000000.
When it is printed with %d, printf() uses only the integer part of $value and it is 1639.
Use round() to get the correct integer value:
printf('%d', round($value, 0));
Or, even better, use number_format() to get the value as string:
echo(number_format($value, 0, '', ''));
Both print 1640.
1 Think of 1/3. It is approximatively 0.3333333333 but one cannot represent it exactly because it has an infinite number of 3 after the decimal dot. In mathematics, 3 * 1/3 = 1 but in real life (and in computers), 3 * 0.333333333 is never 1, no matter how many 3 we put after the decimal dot. The computer programs usually round the value to some number of decimal places and it ends up being displayed as 1 but this doesn't happen always.
When it doesn't happen, another question similar to this one pops up on StackOverflow.

Limiting remainder in php

I have a code in which calculates the Effective interest rate.
when I echo it I get 5.1161897881733 which I want to limit the remainder and output it like 5.11, is there any functions use to limit the remainder in php?
A billion ways to do this. The task for you here is to pick one.
Method 1 - round()
This will just round your number. The exact rules can be found in the PHP.net documentation.
round($someNumber, 2);
Method 2 - floor()
Floor will round the number down
floor($someNumber, 2);
Method 3 - ceil()
Opposite to floor() this will round your number upwards.
ceil($someNumber, 2);
Method 4 - number_format()
This will format any number. number_format() has a gazillion possible inputs in which you can choose decimal characters etc.
// Will round your number to 2 decimals with a . as decimal character
number_format($someNumber, 2, ",", ".");
Feel free to edit and add more options :)
round($result, 0)
The 0 represents the decimal places.
http://php.net/manual/en/function.round.php

Floating point numbers take only two digit after number

Doing an billing application in php, There come price with decimals like 0.576 tried
round()
number_format()
it will give me 0.58 but i want 0.57 only, how can i get in php?
Something like this?
floor(0.576*100)/100
//0.57
Using round with PHP_ROUND_HALF_DOWN wont work as it only affect the way decimal 5 is rounded. It does not truncate the float value.
round(0.576, 2, PHP_ROUND_HALF_DOWN);
//0.58 //Not good
round(0.575, 2, PHP_ROUND_HALF_DOWN);
//0.57 //Good but will not work above this value (e.g 0.0576, 0.0577...)
A fraction higher than .5 is always rounded up. If you already have found round why haven't you looked at the documentation:
http://www.php.net/manual/en/function.round.php
Try:
echo round( 0.676, 2, PHP_ROUND_HALF_DOWN );

¨Showing significant figures

I have a question regarding number formating in PHP.
I have a variable called "average", which is simply an average of a few values. To make it clear I rounded the number to 2 decimal places. Now the problem is, that if the average is for example 2.90, it only shows 2.9. Is there any way of displaying 2 decimal places always? I though I could do it by multiplying the number by 100, rounding it to zero d.p. and then divide by 100 again, but that seems a bit overcomplicated if there is an easier way of doing it.
Maybe you can try the number_format(float $number [, int $decimals = 0 ])?
For more information, take a look at http://php.net/manual/en/function.number-format.php
Format the output with printf
printf("%.1f", $num); // prints 1 decimal place
printf("%.2f", $num); // prints 2 decimal places

Categories