简体   繁体   中英

PHP dropping decimals without rounding up

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?

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.

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>';
?>
$float = 1.505;

echo sprintf("%.2f", $float);

//outputs 1.50

使用 PHP 原生函数bcdiv

echo bcdiv(2.56789, 1, 2);  // 2.56

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.

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"

We can use bc functions if they are available:

echo bcadd(sprintf('%F', 5.445), '0', 2); // => 5.44
echo sprintf('%.2F', 5.445); // => 5.45

you can convert 1.505 to String data type and make use of substring() to truncate last character.
And again convert it in integer .

$num = 118.74999669307;
$cut = substr($num, 0, ((strpos($num, '.')+1)+2));`
// Cut the string from first character to a length of 2 past the decimal.
/ substr(cut what, start, ( (find position of decimal)+decimal itself)+spaces after decimal) )
echo $cut; 

this will help you shorten the float value without rounding it..

I know this is a late answer but here is a simple solution. Using the OP example of 1.505 you can simply use the following to get to 1.50.

function truncateExtraDecimals($val, $precision) {
    $pow = pow(10, $precision);
    $precise = (int)($val * $pow);
    return (float)($precise / $pow); 
}

This manages both positive and negative values without the concern to filter which function to use and lends to correct results without the worry about what other functions might do with the value.

$val = 1.509;
$truncated = sprintf('%.2f', truncateExtraDecimals($val, 2));
echo "Result: {$truncated}";

Result: 1.50

The sprintf is needed to get exactly 2 decimals to display otherwise the Result would have been 1.5 instead of 1.50.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM