int((0.1+0.7)*10) = 7 in several languages. How to prevent this?
What Every Computer Scientist Should Know About Floating-Point Arithmetic
int a = (int) ((0.7 + 0.1) * 10). Why a = 7?
The issue is that neither 0.1 nor 0.7 can be represented exactly as double
:
0.1
gets represented as approximately0.10000000000000000555
.0.7
gets represented as approximately0.69999999999999995559
.
Their sum is approximately 0.79999999999999993339
. Multiplied by ten and truncated, this gives 7
.
What Every Computer Scientist Should Know About Floating-Point Arithmetic is an excellent read on the subject.
Why does this code print a result of '7'?
From PHP.net
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.
PHP acting weird on my program
It is because floating point representation in computers is not exact for some numbers. As already said in the comments, 0.7
is represented internally as 0.699999
or so.
There are two websites that continuously pop up in these kind of questions:
- http://floating-point-gui.de/
- http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
I prefer the first one, as it is a little lighter on the academics. Read that information and you'll understand.
PHP (int) / braces mathematically wrong - Possible Bug?
The cast to integer (and related rounding error) occurs at a different point in the formula
Calculate 0.1 plus 0.7, multiply by 10 and cast to integer;
with the expected rounding error, should result in 7
against
Calculate 0.1 plus 0.7, cast to integer, and multiply by 10
casting 0.1 + 0.7 (ie 0.8) to integer should give 0, which multiplied by 10 is still 0
Wrong Calculation?
It’s normal – There’s a thing called precision while working with floating point numbers. It’s present in most of the modern languages. See here: http://www.mredkj.com/javascript/nfbasic2.html for more information.
((0.1+0.7) * 10)
is probably something like 7.9999999
and (int) 7.9999999 = 7
On the other hand ((0.1+0.8) * 10)
is probably 9.00000001
and (int)9.00000001 = 9
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.
Source: http://php.net/manual/en/language.types.float.php
Adding fractions number yields different result in PHP
This question in php manual:
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....
Type Casting With Arithmetic Operation
In the common double-precision format, numbers are represented with a sign bit, an 11-bit exponent, and a 53-bit fraction portion that is called a significand. The significand is always a 53-bit non-negative integer divided by 252 (which can also be written in binary as one binary digit, a radix point, and 52 more binary digits).
.1 cannot be represented exactly. It is represented with an exponent of -4 and a significand of 7205759403792794 / 252. That is, the closest double
to .1 is 7205759403792794•2-52•2-4 = 0.1000000000000000055511151231257827021181583404541015625.
The closest double
to .7 has a significand of 6305039478318694 / 252 and an exponent of -1; it is 6305039478318694•2-52•2-1 = 0.6999999999999999555910790149937383830547332763671875.
When you add these two numbers, the result is 0.7999999999999999611421941381195210851728916168212890625. This is also not exactly representable in a double
; it has to be rounded to the nearest representable value and, when you multiply by 10, that has to be rounded again. However, you can see that the sum is less than .8. The final result is less than 8, so conversion to an integer truncates it to 7.
The double
nearest .8 is 0.8000000000000000444089209850062616169452667236328125. When you add that to 0.1000000000000000055511151231257827021181583404541015625, the sum is 0.9000000000000000499600361081320443190634250640869140625. As you can see, it is greater than .9. The final result of rounding and multiplying by 10 will be 9 or greater, so conversion to an integer produces 9.
The fact that several other values you tried did not round down is merely happenstance. Every value that is not exactly representable falls somewhere between two representable values, one higher and one lower. Some are closer to the higher value and some are closer to the lower value, and you just happened to pick values that were closer to a higher representable value and were rounded upward.
Related Topics
Cloudflare and Logging Visitor Ip Addresses via in PHP
Custom Pagination View in Laravel 5
Getting Http Code in PHP Using Curl
Laravel Stylesheets and JavaScript Don't Load for Non-Base Routes
Generate Random 5 Characters String
Php7.1 JSON_Encode() Float Issue
How to Build a JSON Array from MySQL Database
PHP MySQL Update If Exist or Insert If Not
Any Decent PHP Parser Written in PHP
How to Write a Stored Procedure Using PHPmyadmin and How to Use It Through PHP
Best Way to Determine If a Url Is an Image in PHP
How to Use PHP to Check If a Directory Is Empty
Adding Attributes to Customer Entity
Include Constant in String Without Concatenating
Chaining Static Methods in PHP