php round not working when I use float 7 / 100 [duplicate] - php

This question already has answers here:
Show a number to two decimal places
(25 answers)
Closed 7 years ago.
My code is very simple, for example, my php version is 5.5.11, this is my sample code:
$result = round(($num / 100), 2); // 0.070000000001
$result = $num / 100; // 0.070000000001
I get the $result is 0.070000000001, and if $num = 3, the $result is correct. And I used var_dump($num), the type is the float. how can I fix it?
edit
I found the reason, but I'm not sure the detail. I use Codeigniter, and I load a library PHPExcel, this is third party lib, when I load it, and I will have this problem, but I'm not sure the reason detail.

As I wrote in comment it is connected with how floats are stored in memory
From manual:
Never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.
http://php.net/manual/en/language.types.float.php
I don't know what you want to do with this float variable but if you want to compare then you need some $epsilon
if(abs($a-$b) < $epsilon)
if you want to round then probably you should ignore last digit. number_format() seems like better solution.

This is the issue with floating numbers.
You can even try bccomp
$a = 1.2 * 3;
if (bccomp($a, 3.6) === 0) {
echo 'equal';
} else {
echo 'not equal';
}
//echoes equal
echo "----------------------";
$a = 1.2 * 3;
if ($a == 3.6) {
echo 'equal';
} else {
echo 'not equal';
}
//echoes not equal

Related

Php comparison integer with double

I got a problem with this script
$total = 0;
$expected_total = 1111;
$i = [85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.48];
foreach ($i as $item) { $total += $item; }
if($total != $expected_total) {
echo json_encode([$total,$expected_total]);
}
The problem is that at the end of the sum the $total should be equal to the $expected_total.
I thought about different number types, I printed the type of the two vars and I was right, one was double and the other was integer, so I converted the integer to double
$expected_total = 1111.00;
but the result is still the same.
The only solution that I could find was comparing the rappresentation of the two numbers, casting them to a string.
if((string)$total != (string)$expected_total) {
echo json_encode([$total,$expected_total]);
}
But obviously this is kind of a hack.
Have you ever had a similar problem? How have you solved it?
PHP version : 5.5.9
Many Thanks
This is not only PHP problem. It is about representation of floating point numbers in memory. Floating numbers has limited precision. PHP uses IEEE 754. Read carefully the manual page and you will understand.
You can find in manual code snippet of how to do it.
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001; //very small number
if(abs($a-$b) < $epsilon) {
echo "true";
}
If you want to check them as integers you could round both values.
You should change the if-statement to the following:
if(round($total) != round($expected_total)) {
echo json_encode([$total,$expected_total]);
}
If you do it like this you will compare the rounded values, which will be the same.
There is a big red label in the PHP Manual, that says:
Floating point numbers have limited precision…
So never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.
In this specific case, you could add the floating numbers using bcadd() and compare the totals using bccomp(). Both functions are provided by the BC Math extension:
foreach ($i as $item) {
$total = bcadd((string) $total, (string) $item, 2);
}
if (bccomp((string) $total, (string) $expected_total, 2) == 0) {
echo json_encode([$total,$expected_total]);
}

PHP how to show all decimal places?

this might be a stupid question but I have searched again and again without finding any results.
So, what I want is to show all the decimal places of a number without knowing how many decimal places it will have. Take a look at this small code:
$arrayTest = array(0.123456789, 0.0123456789);
foreach($arrayTest as $output){
$newNumber = $output/1000;
echo $newNumber;
echo "<br>";
}
It gives this output:
0.000123456789
1.23456789E-5
Now, I tried using 'number_format', but I don't think that is a good solution. It determines an exact amount of decimal places, and I do not know the amount of decimal places for every number. Take a look at the below code:
$arrayTest = array(0.123456789, 0.0123456789);
foreach($arrayTest as $output){
$newNumber = $output/1000;
echo number_format($newNumber,13);
echo "<br>";
}
It gives this output:
0.0001234567890
0.0000123456789
Now, as you can see there is an excess 0 in the first number, because number_format forces it to have 13 decimal places.
I would really love some guidance on how to get around this problem. Is there a setting in PHP.ini which determines the amount of decimals?
Thank you very much in advance!
(and feel free to ask if you have any further questions)
It is "impossible" to answer this question properly - because a binary float representation of a decimal number is approximate: "What every computer scientist should know about floating point"
The closest you can come is write yourself a routine that looks at a decimal representation of a number, and compares it to the "exact" value; once the difference becomes "small enough for your purpose", you stop adding more digits.
This routine could then return the "correct number of digits" as a string.
Example:
<?php
$a = 1.234567890;
$b = 0.123456789;
echo returnString($a)."\n";
echo returnString($b)."\n";
function returnString($a) {
// return the value $a as a string
// with enough digits to be "accurate" - that is, the value returned
// matches the value given to 1E-10
// there is a limit of 10 digits to cope with unexpected inputs
// and prevent an infinite loop
$conv_a = 0;
$digits=0;
while(abs($a - $conv_a) > 1e-10) {
$digits = $digits + 1;
$conv_a = 0 + number_format($a, $digits);
if($digits > 10) $conv_a = $a;
}
return $conv_a;
}
?>
Which produces
1.23456789
0.123456789
In the above code I arbitrarily assumed that being right to within 1E-10 was good enough. Obviously you can change this condition to whatever is appropriate for the numbers you encounter - and you could even make it an optional argument of your function.
Play with it - ask questions if this is not clear.

PHP rounding error with simple multiplication [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 3 years ago.
PHP seems to round incorrectly when using (int) to cast variables. Why?
$multiplier = 100000000;
$value = 0.01020637;
echo (int)($value*$multiplier);
Output: 1020636. (unexpected output)
$multiplier = 100000000;
$value = 0.01020637;
echo ($value*$multiplier);
Output: 1020637. (Expected correct output)
Edit: it gets even worse...
$multiplier = 100000000;
$value = 0.01020637;
echo $temp = ($value*$multiplier);
echo '<br/>';
echo (int)$temp;
Output:
1020637
1020636
Things can get hairy when you're dealing with floats, floating point math (and problems involved) are well understood, but can crop up when you're not expecting them. As seems to have happened here. You could read up on the rules extensively, or use language provided tools when handling floating point arithmetic.
When you care about the precision involved you should use the bcmul() function. It's an "optional" extension, but if you care about precision it starts being required rather quickly.
Example:
multiplier = 100000000;
$value = 0.01020637;
echo (int)($value*$multiplier);
echo "\n";
echo bcmul($value, $multiplier, 0);
Sample: http://ideone.com/Wt9kKb
PHP (especially in 32 bit builds) has problems with floating point numbers. This is why casting float into int can have unpredictable results. See PHP Integer page for more detail. Basically, you're getting tiny imprecisions in the math and that can cause serious problems when trying to do something like ceil()
If you really need the numbers converted to int I would suggest you round the numbers first
$multiplier = 100000000;
$value = 0.01020637;
$temp = round($value*$multiplier);
echo $temp . '<br/>' . (int)$temp;
This works by truncating off the small floating point errors. While bcmath can also do the truncation, it's not part of PHP core and not a good overall solution. Your best bet is to write a rounding routine yourself that can return the precision you're looking for. In the project I work on, that was what we did. We wrote our own rounding function and it fixes the problems you'll run into. Without knowing the specifics of what you're trying to do it's hard to say if that's what you need but it's how we did it without bcmath.
The problem you're seeing is the following:
When multiplying two numbers like this:
$mulitply = 0.1 * 100;
You are not multiplying exactly 100 with 0.1, but with with 0.09999999998...
And when it comes to (int), it converts numbers like 4.999 to 4, so your result 1020636.999999999 becomes 1020636 when counting with (int).
bcmul allows for higher precision
$test = (int) bcmul('100000000', '0.01020637');
echo $test
returns the correct answer.
To round floats in PHP you should use the round() function. Just casting to an integer does not round the value correctly.
First argument is which float (the result of your calculation in this case) to be rounded, second is optional, and specifies the amount of decimals (aka precision) being returned. There is also a third argument, controlling the mode. These can be PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN, PHP_ROUND_HALF_EVEN or PHP_ROUND_HALF_ODD.
Example from php.net/round:
<?php
echo round(3.4); // 3
echo round(3.6); // 4
echo round(3.6, 0); // 4
echo round(1.95583, 2); // 1.96
// With the third element, "mode"
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
?>
An example for your code (live example):
<?php
$multiplier = 100000000;
$value = 0.01020637;
echo intval(round($value*$multiplier)); // Returns 1020637
?>

Strange subtraction result with PHP [duplicate]

This question already has answers here:
php float calculation 2 decimal point
(8 answers)
Closed 9 years ago.
I am working on a project for a client, its a quite simple. But there is one calculation I have to make which is also very simple, its like this example:
$a = (49.95 - 24.95);
if ($a == 25.00) {
echo "TRUE";
}
This equals 25.00 right! But no its returning false???
But if I do this, another example similar to a calculation I need:
$a = (99.95 - 24.95);
if ($a == 75.00) {
echo "TRUE";
}
Then I get true! Am I going mad, or is this a bug???
Like you can read in manual, don't compare float directly. Instead use epsilon.
<?php
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
if(abs($a-$b) < $epsilon) {
echo "true";
}
That's because of the way how PHP stores float internally. You can read about it in manual, eg. here http://pl1.php.net/float
It's not a bug. It's about float number precision. Since float numbers are stored with decimal precision, you can't rely on precise compare operations, like == (equality comparison).
Instead you shoult use precision delta and compare floats like:
$a = (49.95 - 24.95);
$b = 25;
$delta = 1E-13;
if(abs($a-$b)<$delta)
{
echo('TRUE');
}
In PHP, 1E-13 will be enough for using as precision delta. For very simple explanation, see this guide about float numbers and their representation.
$a = (49.95 - 24.95);
if ((int)$a == 25) {
echo "TRUE";
}
At the first example there is a float number which is compared to an int number and at the second there is a float number compared to a float number. The difference occurs because of the floating point precision and its not a bug.
To solve this you can define a d (delta) number which will be the precision of the calculation
and then you could check if the absolute result of x-y will be lower then the precision you defined. Something like this if(abs(x-y) < d)

PHP number format drops zero when rounding [duplicate]

This question already has answers here:
Print numeric values to two decimal places
(6 answers)
Closed 11 months ago.
I'm using PHP's number_format to display floats to 2 decimal places.
When the number is something like 1.898 it gets rounded up to 1.9.
How do I get it to display that as 1.90?
Update:
I have a function that ends...
return number_formant($num, 2);
The php script that calls the function prints out the number to be used by Javascript. When I do a var_dump on the number, it prints correctly with two decimal places. Looks like it's Javascript that's loosing the zero.
Here's the JS code that was causing the issue...
function show_level(level) {
...
if (level > 9999)
level_label = (level / 1000).toPrecision(3) + 'k';
else if (level > 999)
level_label = (level / 1000).toPrecision(2) + 'k';
else
level_label = level;
I altered the last line to get it working how I wanted..
level_label = level.toFixed(2);
Maybe not the best solution but:
$n = 1.2345;
$n = number_format( round($n, 1), 2);
//echo 1.20
echo $n;
So,
you mean something like this (what you have):
<?php
echo number_format($x);
?>
and here is what you want:
<?php
echo number_format($x,2);
?>
you can use other lengths. Try it Out!
Hope could helpya
:)
echo sprintf('%01.2f', number_format(1.898,2))
You can do:
echo round(1.898,2);

Categories