Question

I've been wrestling with PHP's ceil() function giving me slightly wrong results - consider the following:

$num = 2.7*3; //float(8.1)
$num*=10; //float(81)
$num = ceil($num); //82, but shouldn't this be 81??
$num/=10; //float(8.2)

I have a number which may have any number of decimal places, and I need it rounded up to one decimal place. i.e 8.1 should be 8.1, 8.154 should be 8.2, and 8 should be left as 8.

How I've been getting there is to take the number, multiply by 10, ceil() it, then divide by ten but as you can see I'm getting an extra .1 added in some circumstances.

Can anyone tell my why this is happening, and how to fix it?

Any help greatly appreciated

EDIT: had +=10 instead of *=10 :S

EDIT 2: I didn't explicitly mention this but I need the decimal to ALWAYS round UP, never down - this answer is closest so far:

rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');

However rounds 3.84 down to 3.8 when I need 3.9. Sorry this wasn't clearer :(

Final Edit:

What I ended up doing was this:

$num = 2.7*3; //float(8.1)
$num*=10; //float(81)
$num = ceil(round($num, 2)); //81 :)
$num/=10; //float(8.1)

Which works :)

Was it helpful?

Solution

This is more than likely due to floating point error.

You may have luck trying this procedure instead.

<?php
$num = 2.7*3;
echo rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');

OTHER TIPS

Floats can be a fickle thing. Not all real numbers can be properly represented in a finite number of binary bits.
As it turns out, a decimal section of 0.7 is one of those numbers (comes out 0.10 with an infinity repeating "1100" after it). You end up with a number that's ever so slightly above 0.7, so when you multiply by 10, you have a one's digit slightly above 7.

What you can do is make a sanity check. Take you float digit and subtract it's integer form. If the resulting value is less than, say, 0.0001, consider it to be an internal rounding error and leave it as-is. If the result is greater than 0.0001, apply ceil() normally.

Edit: A fun example you can do if you're on windows to show this is to open up the built in calculator application. Put in "4" then apply a square root function (with x^y where y=0.5). You'll see it properly displays "2". Now, subtract 2 from it and you'll see that you don't have 0 as a result. This is caused by internal rounding errors when it attempted to compute the square root of 4. When displaying the number 2 earlier, it knew that those very distant trailing digits were probably a rounding error, but when those are all that's left, it gets a bit confused.

(Before anybody gets onto me about this, I understand that this is oversimplified, but nonetheless I consider it a decent example.)

The problem is that floating point numbers are RARELY what you expect them to be. Your 2.7*3 is probably coming out to be something like 81.0000000000000000001, which ceil()'s up to 82. For this sort of thing, you'll have to wrap your ceil/round/floor calls with some precision checks, to handle those extra microscopic differences.

Use %f instead of %.1f.

echo rtrim(rtrim(sprintf('%f', $num), '0'), '.');

Why not try this:

$num = 2.7*3; 
$num *= 100; 
$num = floor($num); 
$num /= 10; 
$num = ceil($num); 
$num /= 10;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top