PHP dropping decimals without rounding up

前端 未结 13 2162
独厮守ぢ
独厮守ぢ 2020-12-09 15:09

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?

相关标签:
13条回答
  • 2020-12-09 15:47

    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>';
    ?>
    
    0 讨论(0)
  • 2020-12-09 15:49

    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"
    
    0 讨论(0)
  • 2020-12-09 15:50

    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.

    0 讨论(0)
  • 2020-12-09 15:54

    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));
    
    0 讨论(0)
  • 2020-12-09 15:55

    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
    
    0 讨论(0)
  • 2020-12-09 15:55

    just do (int) $number; to get integer

    0 讨论(0)
提交回复
热议问题