0

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

supercoolville
  • 8,636
  • 20
  • 52
  • 69

1 Answers1

4

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).

Note: I read a comment saying round(600.123 * 100); gives 600122 instead of 600123. That happens because 600.123 has more than two decimal digits, and the last one is lesser than 5.

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. Or splitting the integer and decimal sides:

<?php
$integer = 600;
$decimal = 55;
$value = $integer * 100 + $decimal;

The output is: int(60055).

Pedro Amaral Couto
  • 2,056
  • 1
  • 13
  • 15