I have a weird math calculation here. I hope someone will explain.
$a = 1.85/100;
$b = 1.5/100;
$c = 1.1/100;
$d = 0.4/100;
$e = 0.4/100;
$f = 0.4/100;
$g = 0.4/100;
$h = $a + $b + $c + $d + $e + $f + $g;
echo $h*100 ."<br>";
$i = $h-$a;
$i = $i-$b;
$i = $i-$c;
$i = $i-$d;
$i = $i-$e;
$i = $i-$f;
$i = $i-$g;
echo $i;
The last $i value should be 0 but it returns 6.93889390391E-18.
You should read this article:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Floating point arithmetic is not exact. You should expect small errors. The answer is correct to within a small rounding error. If you need to check if a floating point number is zero it is best not to check that it is exactly equal to zero but instead to check if it is sufficiently close to zero.
If you really need exact arithmetic, don't use floating point types. In your example you could multiply all your numbers by 100 and use integer arithmetic to get an exact answer.
any idea to correct my equation to show final result 0.00 ?
Yeah, round($i, 2)
The "discrepancies" are usually so small, that rounding it to 2 decimals will almost always solve the problem.
There is nothing wrong going there, there are simply rounding approximation.
In this very cas you should multiply all your values for 1000 and do a division at the end of the calculation or, better, resort to precise calculation extension
Related
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]);
}
Here is a simple PHP script:
<?php
$a = 100;
$b = 91.51;
$c = 8.49;
$d = $a - $b - $c;
echo $d;
?>
It outputs -5.3290705182008E-15 which is ugly
With minor change as follows:
$d = $a - ($b + $c);
echo $d;
?>
The output is 0 which is correct. Why is this happening?
Floating point maths is actually very inaccurate. It's a "best guess" value ;)
In floating point (single precision) 100 - 91.51 is not 8.49 as you would expect, but 8.4899978638 since the value 8.49 cannot be exactly expressed in floating point. With double precision it gets better, as it equates to 8.48999999999999488409 which is a little closer. Still not exact though.
Take the following code example:
echo (100 - 91.51) . "\n";
echo number_format(100 - 91.51, 20) . "\n";
The output of that you would expect to be
8.49
8.49000000000000000000
But in fact it is:
8.49
8.48999999999999488409
It defaults to rounding to 2 decimal places for printing floating point values.
Floating point is very much a trade-off. It is a numerical representation system that provides increased resolution and range at the cost of accuracy. For accuracy, but with limited resolution and range, fixed point arithmetic is often used. For instance, if you were storing voltage values between 0V and 5V and you wanted precise measurements at 1µV resolution (i.e., 0.000001V divisions) you may choose to instead represent your voltages in microvolts not volts, so a value of 100000 would actually be 0.1V, and 3827498 would be 3.827498 volts. The mathematics at your prescribed resolution become precise, however you lack the ability to then represent 287x1036V without having massive variables to store the value.
Try to use number_format like this:
$a = 100;
$b = number_format(91.51, 0, ".", "." );
$c = number_format(8.49, 0, ".", "." );
$d = $a - $b - $c;
echo $d;
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."
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.
How would you find the fractional part of a floating point number in PHP?
For example, if I have the value 1.25, I want to return 0.25.
$x = $x - floor($x)
$x = fmod($x, 1);
Here's a demo:
<?php
$x = 25.3333;
$x = fmod($x, 1);
var_dump($x);
Should ouptut
double(0.3333)
Credit.
Don't forget that you can't trust floating point arithmetic to be 100% accurate. If you're concerned about this, you'll want to look into the BCMath Arbitrary Precision Mathematics functions.
$x = 22.732423423423432;
$x = bcsub(abs($x),floor(abs($x)),20);
You could also hack on the string yourself
$x = 22.732423423423432;
$x = strstr ( $x, '.' );
The answer provided by nlucaroni will only work for positive numbers. A possible solution that works for both positive as well as negative numbers is:
$x = $x - intval($x)
If if the number is negative, you'll have to do this:
$x = abs($x) - floor(abs($x));
My PHP skills are lacking but you could minus the result of a floor from the original number
However, if you are dealing with something like perlin noise or another graphical representation, the solution which was accepted is correct. It will give you the fractional part from the lower number.
i.e:
.25 : 0 is integer below, fractional part is .25
-.25 : -1 is integer below, fractional part is .75
With the other solutions, you will repeat 0 as integer below, and worse, you will get reversed fractional values for all negative numbers.
Some of the preceding answers are partial. This, I believe, is what you need to handle all situations:
function getDecimalPart($floatNum) {
return abs($floatNum - intval($floatNum));
}
$decimalPart = getDecimalPart($floatNum);
You can use fmod function:
$y = fmod($x, 1); //$x = 1.25 $y = 0.25
To stop the confusion on this page actually this is the best answer, which is fast and works for both positive and negative values of $x:
$frac=($x<0) ? $x-ceil($x) : $x-floor($x);
I ran speed tests of 10 million computations on PHP 7.2.15 and even though both solutions give the same results, fmod is slower than floor/ceil.
$frac=($x<0) ? $x-ceil($x) : $x-floor($x);
-> 490-510 ms (depending on the sign of $x)
$frac=fmod($x, 1);
-> 590 - 1000 ms (depending on the value of $x)
Whereas the actual empty loop itself takes 80 ms (which is included in above timings).
Test script:
$x=sqrt(2)-0.41421356237;
$time_start = microtime(true);
for ($i=0;$i<=9999999;$i++) {
//$frac=fmod($x, 1); // version a
$frac=($x<0) ? $x-ceil($x) : $x-floor($x); // version b
}
$time_end = microtime(true);
$time = $time_end - $time_start;