This question already has answers here:
Compare floats in php
(17 answers)
Closed 8 years ago.
I am new to PHP and trying to perform a simple arithmetic addition and comparison. I have an array with some decimal values and after adding all the values, I am trying to compare it to 1. My array is:
$myArray=[0.2,0.7,0.1]
and my code is:
$sum=0;
foreach($myArray as $val){
$sum+=$val;
}
Here, the sum comes out to be 1. But when I compare it using the following code:
if($sum!=1)
{
echo "Good";
}
else
{
echo "Bad";
}
it echoes "Good".
However, when my array contains values 0.8 and 0.2, it echoes "Bad". Can anyone help me on this?
If you check the PHP docs on floating-point numbers: php.net/manual/en/language.types.float.php, there is a huge warning on the page.
"Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded."
Thus, you could get unexpected results such as 0.2 + 0.7 = 0.900000000001. It's simply because computers have a hard time representing decimals. Binary was really built for integers.
Careful in your logic; I think you meant if($sum == 1) echo "Good", etc. In that case, 0.2+0.7+0.1 would echo "Bad", because it's totally possible that 0.2+0.7+0.1 = 1.00000001 or 0.99999999. On the other hand, adding a smaller number of floats together decreases the error, so 0.8+0.2 is more precise than 0.2+0.7+0.1.
Related
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 5 years ago.
I have this simple substraction code:
<?php
$n1 = 257931.076;
$n2 = 257930;
echo $n1 - $n2;
?>
Why i got 1.0760000000009 instead of 1.076
Where did the 0000000009 came from? i need precise result and i don't want to use round() or number_format() because sometime i have more than 3 decimal, for example: 12345.678912, anyone know?
I have tried to use round() or number_format() but it only for fixed decimal point, not dynamic
As #GordonM perfectly explained it, you cannot expect exact results when using floating point values.
You can use a library such as brick/math to perform exact calculations on decimal numbers of any size:
use Brick\Math\BigDecimal;
$n1 = BigDecimal::of('257931.076'); // pass the number as a string to retain precision!
$n2 = 257930;
echo $n1->minus($n2); // 1.076
The library uses GMP or BCMath internally when available, but can work without these extensions as well (with a performance penalty).
For technical reasons that every programmer should be aware of, IEEE floating point numbers simply can't represent numbers precisely and will use the closest approximation they can when storing them (In fact the only fractions that can be stored perfectly have denominators that are powers of 2 (1/2, 1/4, 1/8, 1/16, etc. All other values are approximations). PHP has an ini value called "precision", which controls how many digits are considered significant WHEN OUTPUTTING floating point values. It defaults to 14, with any digits after that hidden.
However, the actual value stored may try to approximate the desired value with far more digits than that. If you change precision, you'll see what is really being stored.
php > $test = 0.1;
php > var_dump ($test);
php shell code:1:
double(0.1)
php > ini_set("precision", 100);
php > var_dump ($test);
php shell code:1:
double(0.1000000000000000055511151231257827021181583404541015625)
php > var_dump (0.25);
php shell code:1:
double(0.25)
php > var_dump (0.4);
php shell code:1:
double(0.40000000000000002220446049250313080847263336181640625)
What can you actually do about this? Not a great deal, this is just a consequence of how floating point works. You can try to avoid using floating point if you need exact values (for example when dealing with money amounts, store 3.99 as 399 pennies/cents instead of 3.99 pounds/dollars), or you can use the "bugnum" libraries that are available in PHP, GMP and BC_Math, but these are both tricky to use and have their own sets of gotchas. They can also be hard on storage and/or processor time. In most cases it's best to just live with it and be aware that when you're dealing with floating point you're not dealing with an exact representation.
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 6 years ago.
Some strange things are going on in my code. Could someone explain please why is this condition false?
When I output object's members with php's function var_dump(), I get this:
string(6) "105.63"
float(105.63)
from one object, and this is output from another object:
string(6) "667.69"
float(667.69)
Then I do comparison like this:
if(105.63 == "105.63"){
echo "true";
} else {
echo "false";
}
if(667.69 == "667.69"){
echo "true";
} else {
echo "false";
}
it outputs 2 times true for sure, if we write code like in my example above. But in my class, it behaves differently. In first if I get false and in second if I get true. Then I looked to the data more deeply with var_export() function and it seems like I actually have following data:
'105.63'
105.6300000000000096633812063373625278472900390625
and
'667.69'
667.69000000000005456968210637569427490234375
So I decide to check wether my conditions work with that data... And they dont. Actually, only first one doesn't. Why do I get output false and true in following code?
if(105.6300000000000096633812063373625278472900390625 == "105.63"){
echo "true";
} else {
echo "false";
}
if(667.69000000000005456968210637569427490234375 == "667.69"){
echo "true";
} else {
echo "false";
}
I know how to fix this. But I'm interested why conditions fails in first if.
EDIT
Php does type juggling when we use == comparison operator!
Take a look at this:
var_export((float) "105.63");
var_export((string) 105.6300000000000096633812063373625278472900390625);
var_export((float) "667.69");
var_export((string) 667.69000000000005456968210637569427490234375);
outputs:
105.63
'105.63'
667.69000000000005
'667.69'
Should not this mean that the first condition be true and the second false? But I get false in first and true in second. Sorry if I was unclear.
Check the manual: http://php.net/manual/en/language.types.float.php
There is a big warning section about comparing floating point numbers:
WARNING
Floating point precision
Floating point numbers have limited precision. Although it depends on
the system, PHP typically uses the IEEE 754 double precision format,
which will give a maximum relative error due to rounding in the order
of 1.11e-16. Non elementary arithmetic operations may give larger
errors, and, of course, error propagation must be considered when
several operations are compounded. Additionally, rational numbers that
are exactly representable as floating point numbers in base 10, like
0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the
mantissa. Hence, they 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.9999999999999991118.... 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. For a "simple" explanation, see the » floating point guide
that's also titled "Why don’t my numbers add up?"
Why dont my numbers add up?
This question already has answers here:
Why does floating-point arithmetic not give exact results when adding decimal fractions?
(31 answers)
Closed 6 years ago.
I have been scratching my head at this VERY odd problem. I do some calculations in PHP and the end result is a number. This is a whole number, but because calculations are done, PHP considers this a float. However, when I typecast it as an integer, it magically gets subtracted one. As in 1. A whole integer down. I really am at a loss. Try for yourself.
<?php
$number_of_rows = 10;
$number_of_columns = 19;
$active = array();
$tile = 160;
$column = $tile/$number_of_columns; // 8.42105263158
$rounded_down = floor($column); // 8
$column = $column-$rounded_down; // 0.42105263158
$column = $column*$number_of_columns; // 8
var_dump($column); // 8 -> that is great
var_dump((int)$column); // 7 -> WTF?!!!
?>
PHP 7.0.12 on Linux 64 bit.
See the Warning in PHP manual for an explanation.
Excerpt that talks about precision and a floor() example:
Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.
Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they 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.9999999999999991118....
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 6 years ago.
I found somewhere in book
echo (int) ((0.1 + 0.7) * 10);
output is : 7
echo ((0.1 + 0.7) * 10);
output : 8
why both out are different ? I think answer should be 8
When you write
echo ((0.1 + 0.7) * 10);
the result of this simple arithmetic
expression is stored internally as 7.999999 instead of 8.
Now when the value is converted to int,
echo (int) ((0.1 + 0.7) * 10); // 7.999999 when typecasted to int becomes 7
PHP simply truncates away the fractional part, resulting in a rather
significant error (12.5%, to be exact).
It's because float point at this scenario does not fit to memory and is truncated when converting to integer.
Read about that in PHP manual about float
Warning
Floating point precision
Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.
Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they 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.9999999999999991118....
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.
Please check http://php.net/manual/en/language.types.integer.php and find this
Warning : Never cast an unknown fraction to integer, as this can sometimes lead to unexpected results.
<?php
echo (int) ( (0.1+0.7) * 10 ); // echoes 7!
?>
See also the warning about float precision.
This question already has an answer here:
The accuracy of PHP float calculate
(1 answer)
Closed 8 years ago.
I got a problem with PHP.
This exact substraction below is wrongly computed.
<?php
$test = 145.48 - 80.26;
if($test != 65.22)
echo 'not good !';
else
echo 'good';
?>
This echoes "not good" !!!
Why ?
Computers aren't very good at storing floating point (decimal) numbers since representing a base 10 decimal number in binary is hard. For instance, if you try to store the number 0.2 in binary, the computer will store a series following the pattern 0.00110011… . Depending on the size of the floating point number (i.e. how many bits have been allocated for it in memory), the precision will vary, but more important, it will never accurately store exactly 0.2.
There are several ways to fix this, one is using the BC Math library and do something like:
bcsub("145.48", "80.26");
But sometimes the better solution is to just acknowledge that the numbers won't be accurate and account for the error, i.e.
if (abs($x - $y) < $e)
where e is some very small number, e.g. 10^(-5). This is common practice when working with physics calculations and similar, but of course you should never attempt this when working with discrete numbers, e.g. currencies.
To do exact floating number arithmetic you can use bc_math:
$test = bcsub("145.48", "80.26");
assert ($test == "65.22");