PHP dropping decimals without rounding up
Solution 1
You need floor() in this way:
$rounded = floor($float*100)/100;
Or you cast to integer:
$rounded = 0.01 * (int)($float*100);
This way it will not be rounding up.
Solution 2
$float = 1.505;
echo sprintf("%.2f", $float);
//outputs 1.50
Solution 3
To do this accurately for both +ve and -ve numbers you need use:
- the php floor()
function for +ve numbers
- the php ceil()
function for -ve numbers
function truncate_float($number, $decimals) {
$power = pow(10, $decimals);
if($number > 0){
return floor($number * $power) / $power;
} else {
return ceil($number * $power) / $power;
}
}
the reason for this is that floor()
always rounds the number down, not towards zero.
ie floor()
effectively rounds -ve numbers towards a larger absolute value
eg floor(1.5) = 1
while floor(-1.5) = -2
Therefore, for the multiply by power, remove decimals, divide by power
truncate method :
- floor()
only works for positive numbers
- ceil()
only works for negative numbers
To test this, copy the following code into the editor of http://phpfiddle.org/lite (or similar):
<div>Php Truncate Function</div>
<br>
<?php
function truncate_float($number, $places) {
$power = pow(10, $places);
if($number > 0){
return floor($number * $power) / $power;
} else {
return ceil($number * $power) / $power;
}
}
// demo
$lat = 52.4884;
$lng = -1.88651;
$lat_tr = truncate_float($lat, 3);
$lng_tr = truncate_float($lng, 3);
echo 'lat = ' . $lat . '<br>';
echo 'lat truncated = ' . $lat_tr . '<br>';
echo 'lat = ' . $lng . '<br>';
echo 'lat truncated = ' . $lng_tr . '<br><br>';
// demo of floor() on negatives
echo 'floor (1.5) = ' . floor(1.5) . '<br>';
echo 'floor (-1.5) = ' . floor(-1.5) . '<br>';
?>
Solution 4
Use the PHP native function bcdiv
echo bcdiv(2.56789, 1, 2); // 2.56
Solution 5
Maybe it's too late, but here's a good approach:
$getTruncatedValue = function( $value, $precision )
{
//Casts provided value
$value = ( string )$value;
//Gets pattern matches
preg_match( "/(-+)?\d+(\.\d{1,".$precision."})?/" , $value, $matches );
//Returns the full pattern match
return $matches[0];
};
var_dump
(
$getTruncatedValue(1.123,1), //string(3) "1.1"
$getTruncatedValue(1.345,2), //string(4) "1.34"
$getTruncatedValue(1.678,3), //string(5) "1.678"
$getTruncatedValue(1.90123,4) //string(6) "1.9012"
);
- The only pitfall in this approach may be the need to use a Regular Expression (which sometimes could bring a performance penalty).
Note: It's quite hard to find a native approach to truncate decimals, and I think it's not possible to perform that using sprintf and other string-related functions.
Comments
-
newbie almost 4 years
I want to drop off decimals without rounding up. For example if I have 1.505, I want to drop last decimal and value should be 1.50. Is there such a function in PHP?
-
Rene Pot about 12 yearswhat if the number is 1 digit longer after comma?
-
Leigh about 12 yearsWorks fine unless the trailing 0 in the example is a requirement.
-
Jamie Dexter almost 11 yearsWorks beautifully when combined with typecasting
(float) $float
if you're continuing to use this in calculations. -
Jamie Dexter almost 11 yearsIt's important to note that
sprintf
will ROUND floats if it thinks it needs to. For example, take the division31 / 53 = 0.584905[...]
- if we want 3 decimal places, we can dosprintf("%.3f", (31 / 53))
but this does NOT give us 0.584. It gives us 0.585. -
Loenix over 10 yearssprintf rounds the value... sprintf('%.2f', '1.239') => 1.24
-
reignsly about 9 yearsThanks, i want to try this one.
-
Umakant Patil over 8 yearsIt is expected without rounding... 5.555 should be 5.55 not. 5.56
-
Boaz Rymland about 8 yearsFor negative values, use ceil() instead of floor()
-
goredwards about 8 yearsthis is not a complete solution UNLESS you only have positive numbers - you need to use BOTH
floor()
andceil()
if you don't know the sign of the number you are rounding - see this answer -
Muhammed over 7 yearsThis is not an absolutely correct solution.
floor(5.1*100)/100;
yields5.09
, instead of expected5.10
. You can try with 5.100, or 4.1 the same incorrect result. -
Roel Van de Paar about 6 yearsGreat solution!
-
evilReiko about 6 yearsLike @MuhammedM. said, it doesn't work accurately all the time, like this case:
floor(19.99 * 100)
returns1998
instead of1999
. This should not be the correct answer -
evilReiko about 6 yearsThis might be a good solution if you want to drop off all the decimals.
-
Tom over 5 yearsIts taken me quite a while to find this answer, it looks very promising as the solution!
-
Omar Tariq over 5 yearsThis is not the answer. sprintf does round the value.
-
Prince Bhanwra over 4 yearsIt didn't worked correctly for say example input: 2581.68. It returns "2581.67", however expected return value is "2581.68".
-
Adam Whateverson over 4 yearsYou can use my solution with preg_replace to avoid all of the issues above. It just treats it like a string, cuts it and puts it back as a number. See answer further down.
-
Misunderstood almost 4 yearsUse intval() rather than floor().
-
Misunderstood almost 4 yearsreturn(intval($val * $pow) / $pow);
-
dipak_pusti about 3 yearsPerfect. Used it to get 2 digit and applied number_format with 2 to properly show my price. Thanks
-
John Kary about 3 years
bcdiv()
does require the [php.net/manual/en/book.bc.php](BCMath) PHP extension. Most PHP versions have it enabled by default. To check if your PHP has it enabled, look for "BCMath support => enabled" in the output ofphpinfo()
or$ php -i | grep BCMath
-
Danny F almost 3 yearsbut what happens if you don't know how long the decimal is and what happens if you have this scenario: 1.3E+10
-
vincent PHILIPPE over 2 yearsThis should be the accepted answer, I come to this solution also and this approach prevent any form of rounding data
-
mickmackusa about 2 yearsAlso,
echo sprintf()
is an "antipattern". There is absolutely no reason that anyone should ever writeecho sprintf()
in any code for any reason -- it should beprintf()
every time. This answer is not lying, however, that it provides the desired result from the sample input: 3v4l.org/PQoKW Jamie is not wrong either -- 3v4l.org/h37XV and Loenix is correct as well 3v4l.org/SsNUb . So it seems this technique is not reliable.