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?
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>';
?>
The answers of RenaPot, IsisCode, goredwards are not correct.
Because of how float works in computers (in general), float is not accurate.
To replicate the issue:
floor(19.99 * 100); // Outputs 1998 instead of 1999
floor( 5.10 * 100); // Outputs 509 instead of 510
Within PHP internally, 19.99 * 100
results in something like 1998.999999999999999
, which when we do floor
of that, we get 1998
.
Solution:
Solution 1: Use bcmath library (Suggested by @SamyMassoud) if you have it installed (some shared hosting servers may not have it installed by default). Like so:
//floor(19.99 * 100);// Original
floor(bcmul(19.99, 100));// Outputs 1999
Solution 2: String manipulation (my recommendation):
// Works with positive and negative numbers, and integers and floats and strings
function withoutRounding($number, $total_decimals) {
$number = (string)$number;
if($number === '') {
$number = '0';
}
if(strpos($number, '.') === false) {
$number .= '.';
}
$number_arr = explode('.', $number);
$decimals = substr($number_arr[1], 0, $total_decimals);
if($decimals === false) {
$decimals = '0';
}
$return = '';
if($total_decimals == 0) {
$return = $number_arr[0];
} else {
if(strlen($decimals) < $total_decimals) {
$decimals = str_pad($decimals, $total_decimals, '0', STR_PAD_RIGHT);
}
$return = $number_arr[0] . '.' . $decimals;
}
return $return;
}
// How to use:
withoutRounding(19.99, 2);// Return "19.99"
withoutRounding(1.505, 2);// Return "1.50"
withoutRounding(5.1, 2);// Return "5.10"
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"
);
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.
To avoid using ceil, floor and round, just treat it as a string and cut it if necessary. This avoids all the rounding issues.
The code below looks for the first 2 numbers after the dot and singles them out. Then it looks for any numbers that trail that. It then replaces the entire thing with only the 2 numbers it found. If number doesn't match the replace rule, nothing changes.
You could stick this in a function call and pass the function the number and the quantity of numbers you want to keep after the decimal place.
// Shorten number to 2 decimal places without rounding
$num = 2213.145;
$num = floatval(preg_replace("/\.([0-9]{2})[0-9]{0,99}/",".$1",$num));
Its easy and work better.
$value = 12.9987876;
$value_exp = explode(".", $value);
$value_new = floatval($value_exp[0].'.'.substr($value_exp[1],0,4));
echo $value_new; // output will 12.9987
just do (int) $number;
to get integer