Error when subtracting two floating point values in PHP [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Compare floats in php
I have the following piece of code:
$a = 1.49;
$b = 1.50;
echo $b - $a; // Outputs 0.01, which is ok
if (($b - $a) != 0.01) {
echo "Not ok";
} else {
echo "Ok";
}
The problem is that if statement echoes "Not ok", although the subtract result is 0.01.
Any idea why?

The PHP doc on floating point numbers shows how to compare them
As noted in the warning above, testing floating point values for
equality is problematic, due to the way that they are represented
internally. However, there are ways to make comparisons of floating
point values that work around these limitations.
To test floating point values for equality, an upper bound on the
relative error due to rounding is used. This value is known as the
machine epsilon, or unit roundoff, and is the smallest acceptable
difference in calculations.
<?php
// $a and $b are equal to 5 digits of precision.
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
if(abs($a-$b) < $epsilon) {
echo "true";
}
?>
Applied to your example:
$c = $b - $a;
$epsilon = 0.00001;
if (abs($a-$b-0.01) < $epsilon) {
echo "Not ok";
} else {
echo "Ok";
}
Outputs OK

it's because some floating point operations. the result isn't exactly 0.01. your output is rounded by the system.
just try var_dump(($b-$a)-0.01). this should be float(8.673617379884E-18)
a solution would be if (round($b - $a,2) != 0.01)

Related

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

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

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]);
}

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)

Trying to compare a calculation on float values returns false [duplicate]

This question already has answers here:
How is floating point stored? When does it matter?
(9 answers)
Closed 9 years ago.
Why it equals allways false?
<?php
$a = (0.1+0.2);
print $a."\n"; // results in 0.3
if ( (double)$a == (double)0.3 ) {
echo "true";
}else{
echo "not true";
}
echo PHP_EOL;
Perl
perl -e 'if ((0.2+0.1) == 0.3) {print "true\n"; } else { print "false\n"; }'
And now in Python
python -c 'if ((0.2+0.1)!=0.3 ): print "false" '
You need to specify a tolerance [also referred to as epsilon] when comparing floating point values since it is not an exact representation of the number.
function f_cmp(float $a, float $b, float $tol = 0.00001) {
if( abs($a - $b) < $tol ) { return 0; }
else { return $a - $b; }
// return 0 if "equal" within tolerance
// return < 0 if $a < $b
// return > 0 if $a > $b
// for use with PHP functions like usort()
}
Or simply:
function f_eq(float $a, float $b, float $tol = 0.00001) {
if( abs($a - $b) < $tol ) { return true; }
else { return false; }
}
Floating point values have a limited precision. Hence a value might
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly
printing it without any mathematical operations.
If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/
(Standard answer for people who report such bugs at http://bugs.php.net)
Enter this at the Python command line:
>>> 0.2 + 0.1
You'll probably see:
0.30000000000000004
0.2 and 0.1 do not have exact representations in binary floating point. See this link for details:
http://docs.python.org/2/library/decimal.html

PHP Simple Math Calculation

can help me to see this calculation? It suppose echo "equal"... but it give me "not equal"
<?php
$tl_pax = 1;
$ct_pax = 2;
$at_pax = 2;
$a = 0.5;
$b = 0.2;
$c = 0.2;
$d = 0.2;
$e = 0.2;
$f = 0.2;
$g = 0.2;
$h = 0.9;
$sum = $a + $b + $c + $d + ($e * $tl_pax) + ($f * $ct_pax) + ($g * $at_pax) + $h;
$total = 3;
if($total == $sum){
echo 'equal: ' . $sum . ' - ' . $total;
}
else{
echo 'not equal: ' . $sum . ' - ' . $total;
}
?>
This is the usual case of the rounding error associated with binary floating point numbers. There are numbers that can't be represented exactly in binary, and thus the result will be of by some margin. To read up on it, the wikipedia article about floating point numbers is great.
The usual pattern found in this case is to pick a delta and compare against it:
if(abs($total - $sum) < 0.01)
echo "equal";
You'll have to pick your delta appropiately according to the usecase.
Check if they have difference less than 0.00001
if(abs($total - $sum) < 0.00001){
http://sandbox.phpcode.eu/g/56905/6
This article shows you why is this happening
It's because your sum is really something like 2.9999999999999999999, due to floating pont arithmetic. PHP just hides that from you when you print it. See the example on floor((0.1+0.7)*10) here: http://php.net/manual/en/language.types.float.php
You should never compare a floating point number for equality. The proper way to compare floats is using a range like:
if($total-0.0000001 <= $sum && $sum <= $total+0.0000001){
You can see it in action here: http://codepad.org/kaVXM5g0
That line just means that $total must be within 0.0000001 of $sum to be considered equal. You can pick the number yourself, depending on the amount of precision you need.
Alternatively you can just round $sum in this case, but then you're basically doing the same thing just with a range from 2.5 - 3.499... instead of 2.9999999 - 3.0000001
The difference is due to the limits of floating point precision.
Values like 0.9 (9/10) can't be written exactly as binary floating point numbers, just like 0.3333... (1/3) can't be written exactly as a decimal fraction. This means that e.g. $h holds an inexact, rounded representation of 0.9. As a result, your calculation yields something very close to 3, but not exactly 3.
Floats are evil.
Quote from http://php.net/float
"So never trust floating number results to the last digit, and never compare floating point numbers for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available."

Categories