We have an API that returns prices as string in EUR, e.g. 2.55 or 0.035. Our database however saves this in cents, so I wrote a really simple method to transform this into cents by just doing
$cents = (int) ((float) $value['value']['amount'] * 100)
So for this code
echo (int) ((float) '2.55' * 100);
the result should be 255, right? But somehow, it's 254. You can test this on your CLI by simply doing
php -r "echo (int) ((float) '2.55' * 100);"
When I get the result of (float) '2.55' * 100) it's simply 255, and when I cast this to int it's 255, why isn't it the same if I cast it to int in the same call?
I mean, I could simply do '2.55' * 100 and the php casting would calculate this correctly, but I'm still curious why this happens?
Version:
PHP 7.1.15-1+ubuntu16.04.1+deb.sury.org+2 (cli) (built: Mar 6 2018 11:10:13) ( NTS )
It could be a floating point error.
In php, a float value like 255 could actually be something like 254.9999991. You can round the value before converting to int.
echo (int) ( round( (float) '2.55' * 100 ) );
This will result to
255
From PHP Doc:
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.
http://php.net/manual/en/language.types.float.php
Related
This question already has answers here:
Why does `intval(19.9 * 100)` equal `1989`?
(5 answers)
Closed 4 years ago.
i am doing some calculation where i noticed intval weird behavior
which is making a whole lot of mess because i just want int part of whole variable but intval is stabbing me in the back in comparisons. Any ideas why its behaving like that and what i can use for my requirement?
intval( 9.62 * 100 ) //gives 961
(int)( 9.62 * 100 ) // gives 962.0
my tried methods to achieve my goals were:
floor(9.62 * 100) // but its giving 962.0 no acceptable
This should work for you.
intval(strval( 9.62 * 100));
It seems to be a problem with the precision of floating points, and converting the number to string and then to int seems to fix the problem.
Here is a link to the documentation, http://php.net/manual/en/language.types.float.php
As it states,
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....
echo intval((0.1 + 0.7) * 10); //returns 7
echo intval(strval(0.1 + 0.7) * 10); //returns 8
Since (int) always rounds the number down, a small error in the representation makes the cast round it one number down that you would otherwise expect.
I tried sprintf('%.40F', 9.62 * 100.0);, and get this: "961.9999999999998863131622783839702606201172".
Thats why intval( 9.62 * 100 ) gives you 961.
You can try to use bcmul()
(int) bcmul(9.62, 100);
Please try this
ceil(9.62*100);
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.
When I'm executing the following code in PHP (v5.5.9) something unexpected happens:
$valueAsCents = 54780 / 100 * 100;
var_dump($valueAsCents);
var_dump((int) $valueAsCents);
This returns
float 54780
int 54779
So apparently the float value with no decimals, is not equal to the int value. Any ideas of what's going on here?
When you divide $valueAsCents = 54780 / 100 then it becomes a float which is not always accurate in digital form because of the way they are stored. In my tests I got
547.7999999999999545252649113535881042480468750000
When multiplied by 100 this is would be
54779.9999999999927240423858165740966796870000
When PHP casts to int, it always rounds down.
When converting from float to integer, the number will be rounded towards zero.
This is why the int value is 54779
Additionally, the PHP manual for float type also includes a hint that floating point numbers may not do what you expect.
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....
if I try to print
echo ((0.1 + 0.7) * 10);
output (http://codepad.org/m3mNNO77)
8
but if I try to print
echo (int)((0.1 + 0.7) * 10);
output (http://codepad.org/8OTCnlVG)
7
why two different results ?
If you perform the echo without the (int) cast, it resulits in:
echo ((0.1 + 0.7) * 10);
8
So what happens is that the floating point actually represents it as 7.99999....
When you perform an (int) cast, the default behavior is to take the integral part, so that's 7. Although it is extremely close to 8, it is not the behavior of a cast to round off.
The errors are due to the fact that floating point numbers are represented with a binary radix. Representing 0.8 correctly is thus impossible. Floating points round off the answers, and hope it doesn't bother that much. When you represent the floating point like 0.8, the result is 0.80000...
Proof of concept using the PHP interactive shell (php -a):
$ php -a
Interactive mode enabled
php > echo number_format(((0.1 + 0.7) * 10),20)."\n";
7.99999999999999911182
php > echo number_format(0.8,20);
0.80000000000000004441
As you may be aware, floating point math is not exact due to restrictions in their representation.
(0.1 + 0.7) * 10 works out to something like 7.99999999999....
When echo'd out, the number is converted to a string using rounding, which uses the php.ini precision setting (15 by default, I believe) to limit the number of decimal numbers. It happens that this rounding gives exactly 8.
However, (int) casts the float to an integer instead, and this is done with truncation. It doesn't matter how close to the next integer up you are, it will always round down. This is why you get 7 here.
I recently start to learn PHP.
<?php
echo (int) ( (0.1+0.7) * 10 ); // prints '7', why not '8' ?
?>
Please convince me of this type-conversion process.
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.
You are running into floating point inaccuracy.
0.1 + 0.7 is not exactly 0.8, but slightly less. The cast to int simply truncates the value and yields 7 as the result.
To get the correct result use rounding:
<?php
echo (int) round( (0.1+0.7) * 10 );
?>
From http://php.net/manual/en/language.types.float.php
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.
As others have said, it's due to a loss of precision, as revealed by:
<?php
printf("%30.27f\n", (0.1 + 0.7) );
printf("%d\n", (int) (0.1 + 0.7) );
printf("%30.27f\n", ( (0.1+0.7) * 10 ) );
printf("%d\n", (int) ((0.1 + 0.7) * 10) );
?>
which outputs:
0.799999999999999933386618522
0
7.999999999999999111821580300
7
You're probably seeing a floating point rounding error, and when you int() it, it goes from being 7...9 to 7.
I am not sure, but this is probably because the int cast is truncating.
.1 + .7 might result in .799999999999999999999999 and
multiplying this by 10 = 7.9999999999999999999
(int) truncates this to 7.