PHP number calculation, strange result - php

For the following
echo 0.02-0.01-0.06+0.05;
I got result is
6.93889390391E-18
For this one
echo -0.02+0.02;
I got result
0
I want to know why i got the different result for those to. I suppose to get two 0 result.

That's how floating point numbers work.

floating point arithmetic doesn't really correlate strongly to normal mathematical rules. For example, operations aren't associative or distributive.
floating point representations are approximations with limited precision.
this isnt a php specific thing - computers simply cant represent numbers like we conceptualize them.

When you write numerals “0.02” in source code, the compiler or interpreter uses floating-point to represent them. The most common floating-point format is IEEE 754 binary floating-point, either single-precision or double-precision.
This floating-point format cannot represent most decimal numerals exactly. For example, just as decimals cannot represent 1/3 exactly (it is approximately .3333, but any decimal numeral of finite length ends somewhere, so it differs slightly from 1/3), the binary numerals used in floating-point cannot represent 1/10 exactly (in binary, 1/10 is approximately .000110011001100110011).
Therefore, when you write “0.02-0.01-0.06+0.05”, the computer is not doing arithmetic with exactly those numbers. It is using close approximations. So the result you get is a close approximation.
If you want an exact result, you must either learn the details of floating-point arithmetic so that you can craft operations that avoid error or for which the error can be corrected or you should use only integer arithmetic. For example, if the above represented dollars amounts to be added and subtracted, you could instead store penny amounts and calculate “2-1-6+5”.

If you round it to two or three decimal places, this will fix it for you.

Hi just for someone hiting this problem, you can use the arbitrary precision functions from BCMath library
http://php.net/manual/en/ref.bc.php
echo 12606.69 + 3053.46 - 15660.15;
1.8189894035459E-12
and
echo bcadd((12606.69 + 3053.46),-15660.15);
0

Related

Laravel issue when printing a double field coming from the database

I have an issue with laravel or php (don´t know yet), the issue is the next:
I have a field on my database called debe which value is 3.97 as you can see below. This field is a double(11,2).
When I access to that field on my framework, for example using a dd() it returns the value correctly as you can see below.
The main problem occurs when i'm trying to print into the view, I receive the next:
I dont know why this is happening but right now the only solution that I've found is to round the value using PHP round() function.
Regards
Check this article in the official documentation:
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.
You can use the round or number_format functions to get the desired decimals

PHP calculating with negative numbers gives wrong values

My PHP code:
echo -100.35+100.00;
echo '<br/>';
echo -478.35+478.32+0.03;
Gives output:
-0.34999999999999
-2.9559688030645E-14
I don't understand why, i tried (float) in front of the values but no difference.
You should use BC Math Functions for such calculations.
Take a look at this manual, it explains that the internal representation is not 100% exact for floats... it has to do with the internal representation of the floats I suspect.
The manual does also refer to these methods and also these for calculations with need of precision.
This is how floats work. In PHP, there are integers, or there are floats - no exact decimals or the like.
Floats are simply not EXACT - there isn't too much you can do about it. http://www.php.net/manual/en/language.types.float.php
Note the big warning:
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....

Why FLOAT != FLOAT

$a = 100;
$b = 3;
$test1 = $a/ $b;
$test2 = 33.333333333333; // $test2 == $test1
var_dump(($test1 * $b)); // float(100)
var_dump(($test2 * $b)); // float(99.999999999999)
Any explanation for that ?
100/3 results in 33.3-repeating.
There is no decimal point value that will ever show it. In maths, such a number is normally shown by a little dot over the repeating value. (though I have no idea how to show that in this code box).
Refer to this wiki article to see how they are represented in various places and a much more detailed explanation that I have given here.
However, from the article here is a snippet of the summary:
In arithmetic, repeating decimal is a way of representing a rational number. Thus, a decimal representation of a number is called a repeating decimal (or recurring decimal) if at some point it becomes periodic, that is, if there is some finite sequence of digits that is repeated indefinitely. For example, the decimal representation of 1/3 = 0.3333333… or 0.3 (spoken as "0.3 repeating", or "0.3 recurring") becomes periodic just after the decimal point, repeating the single-digit sequence "3" infinitely. A somewhat more complicated example is 3227/555 = 5.8144144144…, where the decimal representation becomes periodic at the second digit after the decimal point, repeating the sequence of digits "144" indefinitely.
Now, that's the crux of your issue here, but also keep in mind (as the other two answers point out) that floats are amazingly innacurate in terms of calculations and comparisons. A quick search of this site will reveal a small army of people that have had problems with the internal representations of floats - mainly which resulted in unexpected behavior when doing comparisons.
Take a long read of the PHP warning on the float data type - again I have copied the important bit here:
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....
Not all floats can be represented exactly by processor. $a/$b is one of them.
So, $test1 != $test2 is true.
Information from PHP
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.
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.
Many thanks to all who took the time to answer my question

Why would on earth PHP convert the result of (int) ((0.1+0.7)*10) to 7, not 8?

Why would on earth PHP convert the result of (int) ((0.1+0.7)*10) to 7, not 8? I know that the result will be (float) 8 when it's cast to (int) the result will be 7? Why does that happen?
It is all about float. See PHP: 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.
Additional reading:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
How dangerous is it to compare floating point values?
Why can't decimal numbers be represented exactly in binary?
Halley's answer is great.
The additional info you need is that casting to int always truncates the decimals, no matter how close they are to the next number. So (int) 7.999999999999 will be 7.
It is often customary to round as you cast to get to the nearest, for example
(int) (x + 0.5)
There are many functions that will handle floating point and rounding, truncating, etc. You need to read them and choose the one that fits your needs.

why 0.1+0.2-0.3= 5.5511151231258E-17 in php [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
The accuracy of PHP float calculate
when i executed the code below in eclipse , the result was not 0 but 5.5511151231258E-17
$a = 0.1+0.2-0.3;
echo $a;
could someone tell me why?
This is because floating point numbers have limited precision.
You can find more information about this trait on this page in the PHP manual.
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 progragation 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 never
compare floating point numbers for equality. If higher precision is
necessary, the arbitrary precision math functions and gmp functions
are available.
Please note that this is not a trait specific to PHP; it is just the way floating point numbers work.
You're running into PHP's floating point precision issues. All languages have them, you've just found PHP's.
This is not PHP specific, as others have mentioned. However, you can avoid limited floating point precision by using round():
<?php
$a = 0.1+0.3-0.2;
echo round($a, 2);
?>
Of course you will need to know the number of digits beforehand.
It's because computers can't accurately represent floating point numbers. This isn't specific to PHP.
See here for details.

Categories