I was trying to multiply floats containing decimals and was getting incorrect results.
For example, the following code does not calculate correctly....
$value = (int) (600.55 * 100);
var_dump($value); // outputs the number 60054 when it should be 60055
I was able to solve the issue by removing the decimal with str_replace...
$value = str_replace('.', '', 600.55);
var_dump($value); // now outputs the correct number 60055
But I was wondering if there is a more proper way to convert a decimal (float) to an integer so I can do basic math operations without having the numbers change
CodePudding user response:
Let's understand why you get 60054
.
<?php
$value = 600.55 * 100;
printf('%.18f', $value);
The output is: 60054.999999999992724042
.
$value
is a floating-point number. Its value might not be represented in memory as you expect. It's an approximation. That's why, for instance, it's a very bad idea to use floating-point numbers for money. Probably it's fine for geographic coordinates.
Why $value = str_replace('.', '', 600.55);
seems to work properly?
<?php
echo 600.55;
The output is: 600.55
.
<?php
printf('%.18f', 600.55);
The output is: 600.549999999999954525
.
<?php
$value = 600.55;
printf('%s => %.18f', $value, str_replace('.', '', $value));
The output is: 600.55 => 60055.000000000000000000
.
<?php
echo str_replace('.', '', 600.5599999999999);
The output is: 60056
.
When a floating-point is cast to a string, the string doesn't have all decimal digits. You'll get an “educated guess”. In your case, 600.55
was implicitly cast to "600.55"
because the arguments of the function str_replace
are expected to be strings.
So what should you do?
If you only want to multiply a number by 100, it seems rounding works fine:
<?php
$value = (int)round(123.45 * 100);
var_dump($value);
The output is: int(12345)
.
But if you're performing lots of operations, it might fail:
$value = 0;
for ($i = 0; $i < 50; $i) {
$value = .2;
}
var_dump((int)$value, .2 * 50);
The output is: int(9) float(10)
.
There's a mathematical extension, BCMath, to handle operations with decimal numbers that require precision. It uses strings to represent numbers:
<?php
$value = bcmul('600.55', '100');
var_dump($value);
The output is: string(5) "60055"
.
If performance is a priority, you may also implement a way to represent fixed point numbers, splitting the integer and decimal sides:
<?php
$integer = 600;
$decimal = 55;
$value = $integer * 100 $decimal;
The output is: int(60055)
.