42

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
  • 24,681
  • 7
  • 68
  • 92
newbie
  • 24,286
  • 80
  • 201
  • 301

13 Answers13

50

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.

hakre
  • 193,403
  • 52
  • 435
  • 836
Rene Pot
  • 24,681
  • 7
  • 68
  • 92
  • Works fine unless the trailing 0 in the example is a requirement. – Leigh Jan 31 '12 at 12:13
  • 1
    For negative values, use ceil() instead of floor() – Boaz Rymland Mar 17 '16 at 15:11
  • 1
    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](http://stackoverflow.com/a/28821326/3092596) – goredwards Apr 26 '16 at 16:49
  • 1
    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. – Muhammed Jan 05 '17 at 18:53
  • 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 Apr 10 '18 at 12:31
  • It didn't worked correctly for say example input: 2581.68. It returns "2581.67", however expected return value is "2581.68". – Prince Bhanwra Nov 13 '19 at 23:22
  • 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. – Watts Epherson Jan 22 '20 at 10:25
  • Use intval() rather than floor(). – Misunderstood Jul 04 '20 at 14:40
17

Use the PHP native function bcdiv

echo bcdiv(2.56789, 1, 2);  // 2.56
paovivi
  • 181
  • 1
  • 3
  • 2
    Perfect. Used it to get 2 digit and applied number_format with 2 to properly show my price. Thanks – dipak_pusti Mar 18 '21 at 03:08
  • `bcdiv()` does require the [https://www.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` – John Kary Apr 06 '21 at 16:27
12

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>';
?>
goredwards
  • 2,486
  • 2
  • 30
  • 40
11
$float = 1.505;

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

//outputs 1.50
IsisCode
  • 2,490
  • 18
  • 20
  • Works beautifully when combined with typecasting `(float) $float` if you're continuing to use this in calculations. – Jamie Dexter Jun 11 '13 at 16:20
  • 11
    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. – Jamie Dexter Jun 13 '13 at 14:43
  • 5
    sprintf rounds the value... sprintf('%.2f', '1.239') => 1.24 – Loenix Jan 20 '14 at 14:04
  • 1
    It is expected without rounding... 5.555 should be 5.55 not. 5.56 – Umakant Patil Sep 03 '15 at 08:02
  • 3
    This is not the answer. sprintf does round the value. – Omar Tariq Jan 11 '19 at 13:45
  • 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: https://3v4l.org/PQoKW Jamie is not wrong either -- https://3v4l.org/h37XV and Loenix is correct as well https://3v4l.org/SsNUb . So it seems this technique is not reliable. – mickmackusa Apr 08 '22 at 09:40
4

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.

Telematica
  • 39
  • 3
4

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"
evilReiko
  • 19,501
  • 24
  • 86
  • 102
2

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
user2434435
  • 127
  • 5
2

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

Chirag Lukhi
  • 1,528
  • 17
  • 41
1
$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..

Sujjad A
  • 124
  • 1
  • 5
  • Its taken me quite a while to find this answer, it looks very promising as the solution! – Tom Sep 09 '18 at 20:07
  • but what happens if you don't know how long the decimal is and what happens if you have this scenario: 1.3E+10 – Danny F Jul 15 '21 at 14:27
1

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

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.

0

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));
Watts Epherson
  • 692
  • 5
  • 9
-3

just do (int) $number; to get integer

edorian
  • 38,542
  • 15
  • 125
  • 143
wyknzo
  • 81
  • 2
  • 10