PHP dropping decimals without rounding up

52,722

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.

Share:
52,722
newbie
Author by

newbie

Java developer

Updated on June 16, 2020

Comments

  • newbie
    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
    Rene Pot about 12 years
    what if the number is 1 digit longer after comma?
  • Leigh
    Leigh about 12 years
    Works fine unless the trailing 0 in the example is a requirement.
  • Jamie Dexter
    Jamie Dexter almost 11 years
    Works beautifully when combined with typecasting (float) $float if you're continuing to use this in calculations.
  • Jamie Dexter
    Jamie Dexter almost 11 years
    It's important to note that sprintf will ROUND floats if it thinks it needs to. For example, take the division 31 / 53 = 0.584905[...] - if we want 3 decimal places, we can do sprintf("%.3f", (31 / 53)) but this does NOT give us 0.584. It gives us 0.585.
  • Loenix
    Loenix over 10 years
    sprintf rounds the value... sprintf('%.2f', '1.239') => 1.24
  • reignsly
    reignsly about 9 years
    Thanks, i want to try this one.
  • Umakant Patil
    Umakant Patil over 8 years
    It is expected without rounding... 5.555 should be 5.55 not. 5.56
  • Boaz Rymland
    Boaz Rymland about 8 years
    For negative values, use ceil() instead of floor()
  • goredwards
    goredwards about 8 years
    this is not a complete solution UNLESS you only have positive numbers - you need to use BOTH floor() and ceil() if you don't know the sign of the number you are rounding - see this answer
  • Muhammed
    Muhammed over 7 years
    This is not an absolutely correct solution. floor(5.1*100)/100; yields 5.09, instead of expected 5.10. You can try with 5.100, or 4.1 the same incorrect result.
  • Roel Van de Paar
    Roel Van de Paar about 6 years
    Great solution!
  • evilReiko
    evilReiko about 6 years
    Like @MuhammedM. said, it doesn't work accurately all the time, like this case: floor(19.99 * 100) returns 1998 instead of 1999. This should not be the correct answer
  • evilReiko
    evilReiko about 6 years
    This might be a good solution if you want to drop off all the decimals.
  • Tom
    Tom over 5 years
    Its taken me quite a while to find this answer, it looks very promising as the solution!
  • Omar Tariq
    Omar Tariq over 5 years
    This is not the answer. sprintf does round the value.
  • Prince Bhanwra
    Prince Bhanwra over 4 years
    It didn't worked correctly for say example input: 2581.68. It returns "2581.67", however expected return value is "2581.68".
  • Adam Whateverson
    Adam Whateverson over 4 years
    You 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
    Misunderstood almost 4 years
    Use intval() rather than floor().
  • Misunderstood
    Misunderstood almost 4 years
    return(intval($val * $pow) / $pow);
  • dipak_pusti
    dipak_pusti about 3 years
    Perfect. Used it to get 2 digit and applied number_format with 2 to properly show my price. Thanks
  • John Kary
    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 of phpinfo() or $ php -i | grep BCMath
  • Danny F
    Danny F almost 3 years
    but 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
    vincent PHILIPPE over 2 years
    This should be the accepted answer, I come to this solution also and this approach prevent any form of rounding data
  • mickmackusa
    mickmackusa about 2 years
    Also, echo sprintf() is an "antipattern". There is absolutely no reason that anyone should ever write echo sprintf() in any code for any reason -- it should be printf() 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.