Strange subtraction result with PHP [duplicate] - php

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)

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

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

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)

Less than operator not working correctly in PHP 5.3.1

For the purpose of the example, var 2 is preset from a database as "147.22" type STRING. var 1 is calculated previously in the script and has 147.22 type FLOAT.
Script:
<?
$var1 = (float)$var1;
$var2 = (float)$var2;
var_dump($var1);
var_dump($var2);
if($var1 < $var2) { echo "hello"; }
?>
My expected results would be that the script NOT echo "hello" since the two values are equal in amount and type.
However here is the output I'm getting:
float(197.22)
float(197.22)
hello
If I do not mess with the types and leave the one a float and the other a string, then it still does not work (this is how I got here in the first place).
If i force the values at the time of execution like this:
$var1 = 147.22;
$var2 = 147.22;
var_dump($var1);
var_dump($var2);
if($var1 < $var2) { echo "hello"; }
?>
I get this, (and it works):
float(197.22)
float(197.22)
Notice no "hello"....
Anyone have any clue wth is going on here?
If one of the floats is calculated numerically, and one is created from string assignment, they could be different. Try the following:
$x = 147.22;
$y = 147.2200000000001;
printf("%.40f\n", $x);
printf("%.40f\n", $y);
var_dump($x);
var_dump($y);
var_dump($x < $y);
outputs
147.2199999999999988631316227838397026062012
147.2200000000001125499693443998694419860840
float(147.22)
float(147.22)
bool(true)
Cast them to a string with a specified precision for comparison.
If you are dealing with floats, then it's not safe to compare them directly, because there may be rounding or representation issues.
You'd better to check if the difference between those numbers is less than some predefined and very minimal epsilon, and then determine if they're equal, or which is the greater one.
This discussion may be worth reading: Is casting to float destructive?
EDIT:
More discussions to read:
PHP: Floating point numbers
php integer and float comparison mismatch

PHP does not find two equal numbers to be equal? bug? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
compare floats in php
i have a condition:
if($x <= .3)
echo 1;
it will echo 1 only if x is less than .3
if $x is EQUAL to .3, i do not get a 1.
i have tried wrapping the x in floatval(), but no luck
i tried to echo the $x and i get "0.3"
i have tried if ($x == .3) - nothing
if i have tried if (.3 == .3) which obviously works
any ideas? is this a PHP bug?
It's all about binary representation of floating point numbers :
var_dump(sprintf("%.40f", 0.3));
// string(42) "0.2999999999999999888977697537484345957637"
Basically, 0.3 can't be represented exactly in base 2, so it gets truncated after a few digits. It's basically like 1/3 in base 10 : you can type 0.3, but 0.33 is more precise, so is 0.333, 0.3333, etc. You can't represent it exactly.
Floating point values are not exact. You can check to see if it's roughly <= 0.3 like this:
if ($x <= 0.3000001) {
echo 'yay';
}
Here you go, big, red and fat: Floating Point Numbers:
It is typical that simple decimal
fractions like 0.1 or 0.7 cannot be
converted into their internal binary
counterparts without a small loss of
precision. This can lead to confusing
results: for example,
floor((0.1+0.7)*10) will usually
return 7 instead of the expected 8,
since the internal representation will
be something like 7.9.
This is due to the fact that it is
impossible to express some fractions
in decimal notation with a finite
number of digits. For instance, 1/3 in
decimal form becomes 0.3.
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.
PS: There is even more fun:
INF == INF => false
INF < INF => true
INF > INF => true
So infinity is not infinity and infinity is smaller than infinity and greater than infinity at the same time. If you think about it, it does actually make some sense...
hmmm, i have to assume that your var $x is really not equal to .3.
i just tested this:
<?
$var = 0.3;
if( $var <= .3 )
{
echo 'yay';
}
else
{
echo 'boo';
}
?>
and it outputs yay
$x = .4;
if ($x <= .3) { echo 1; }
Works fine here...tried different values at $x
however... where are you getting $x ?? you might need to "round" the value before comparing.
Floating points are not 100% accurate. In short, the fractional component is generally stored by adding 1/(2^n) together. e.g., 1/2 + 1/4 is how 3/4 would be stored. So this isn't a bug, nor is it a specific PHP question.
However, this should always be true:
$x = 0.3;
if ($x == 0.3) echo "true";
because the same inaccuracy would be present in both.
But this is not guaranteed to be true:
$x = 0.1;
$y = 0.2;
if ($x + $y == 0.3) echo "true";
A simple way to work around this is to use a delta:
if (abs($a - $b) < $delta) echo "true"
where $delta is a very small number.
If you need accuracy, then check out something like the BCMath extension.
If this is for money, then it's usually easier to just do calculations in whole cents (integers), where $1.23 = 123.

Categories