Search code examples
phpprecision

PHP Rounding Float


I'm working on a system where I need to round down to the nearest penny financial payments. Naively I thought I would multiply up by 100, take the floor and then divide back down. However the following example is misbehaving:

echo 1298.34*100;

correctly shows:

129834

but

echo floor(1298.34*100);

unexpectedly shows:

129833

I get the same problem using intval for example.

I suspect the multiplication is falling foul of floating point rounding. But if I can't rely on multiplication, how can I do this? I always want to round down reliably, and I don't need to take negative amounts into consideration.

To be clear, I want any fractional penny amounts to be stripped off:

1298.345 should give 1298.34
1298.349 should give 1298.34
1298.342 should give 1298.34

Solution

  • Since you mention you only use this for displaying purposes, you could take the amount, turn it into a string and truncate anything past the second decimal. A regular expression could do the job:

    preg_match('/\d+\.{0,1}\d{0,2}/', (string) $amount, $matches);
    

    This expression works with any number of decimals (including zero). How it works in detail:

    • \d+ matches any number of digits
    • \.{0,1} matches 0 or 1 literal dot
    • \d{0,2} matches zero or two digits after the dot

    You can run the following code to test it:

    $amounts = [
        1298,
        1298.3,
        1298.34,
        1298.341,
        1298.349279745,
    ];
    foreach ($amounts as $amount) {
        preg_match('/\d+\.{0,1}\d{0,2}/', (string) $amount, $matches);
        var_dump($matches[0]);
    }
    

    Also available as a live test in this fiddle.