Delete digits after two decimal points, without rounding the value

77,343

Solution 1

TL;DR:

The PHP native function bcdiv seems to do precisely what is required, and properly.

To simply "truncate" a number, bcdiv($var, 1, 2); where 2 is the number of decimals to preserve (and 1 is the denomenator - dividing the number by 1 allows you to simply truncate the original number to the desired decimal places)

Full Answer (for history)

This turns out to be more elusive than one might think.

After this answer was (incorrectly) upvoted quite a bit, it has come to my attention that even sprintf will round.

Rather than delete this answer, I'm turning it into a more robust explanation / discussion of each proposed solution.

number_format - Incorrect. (rounds)
Try using number format:

$var = number_format($var, 2, '.', '');  // Last two parameters are optional
echo $var;
// Outputs 2.50

If you want it to be a number, then simply type-cast to a float:

$var = (float)number_format($var, 2, '.', '');

Note: as has been pointed out in the comments, this does in fact round the number.

sprintf - incorrect. (sprintf also rounds)
If not rounding the number is important, then per the answer below, use sprintf:

$var = sprintf("%01.2f", $var);

floor - not quite! (floor rounds negative numbers)

floor, with some math, will come close to doing what you want:

floor(2.56789 * 100) / 100; // 2.56

Where 100 represents the precision you want. If you wanted it to three digits, then:

floor(2.56789 * 1000) / 1000; // 2.567

However, this has a problem with negative numbers. Negative numbers still get rounded, rather than truncated:

floor(-2.56789 * 100) / 100; // -2.57

"Old" Correct answer: function utilizing floor

So a fully robust solution requires a function:

function truncate_number( $number, $precision = 2) {
    // Zero causes issues, and no need to truncate
    if ( 0 == (int)$number ) {
        return $number;
    }
    // Are we negative?
    $negative = $number / abs($number);
    // Cast the number to a positive to solve rounding
    $number = abs($number);
    // Calculate precision number for dividing / multiplying
    $precision = pow(10, $precision);
    // Run the math, re-applying the negative value to ensure returns correctly negative / positive
    return floor( $number * $precision ) / $precision * $negative;
}

Results from the above function:

echo truncate_number(2.56789, 1); // 2.5
echo truncate_number(2.56789);    // 2.56
echo truncate_number(2.56789, 3); // 2.567

echo truncate_number(-2.56789, 1); // -2.5
echo truncate_number(-2.56789);    // -2.56
echo truncate_number(-2.56789, 3); // -2.567

New Correct Answer

Use the PHP native function bcdiv

echo bcdiv(2.56789, 1, 1);  // 2.5
echo bcdiv(2.56789, 1, 2);  // 2.56
echo bcdiv(2.56789, 1, 3);  // 2.567
echo bcdiv(-2.56789, 1, 1); // -2.5
echo bcdiv(-2.56789, 1, 2); // -2.56
echo bcdiv(-2.56789, 1, 3); // -2.567

Solution 2

floor(2.500000550 * 100) / 100;

This should do your task...

Solution 3

You're requesting a function that returns "2.50" and not 2.5, so you aren't talking about arithmetic here but string manipulation. Then preg_replace is your friend:

$truncatedVar = preg_replace('/\.(\d{2}).*/', '.$1', $var);

// "2.500000050" -> "2.50", "2.509" -> "2.50", "-2.509" -> "2.50", "2.5" -> "2.5"

If you want to do it with arithmetic, simply use:

$truncatedVar = round($var * 100) / 100);

// "2.500000050" -> "2.5", "2.599" -> "2.59", "-2.599" -> "2.59"

Solution 4

try with number_format:

echo number_format('2.50000050', 2); // 2.50

Solution 5

number_format rounds the number

php > echo number_format(128.20512820513, 2)."\n";
128.21

I used preg_replace to really cut the string

php > echo preg_replace('/(\.\d\d).*/', '$1', 128.20512820513)."\n";
128.20
Share:
77,343
air
Author by

air

Updated on July 09, 2022

Comments

  • air
    air almost 2 years

    i have value in php variable like that

    $var='2.500000550';
    echo $var
    

    what i want is to delete all decimal points after 2 digits.

    like now value of variable will be

    $var='2.50';
    echo $var
    

    keep in mind this value is coming from mysql databse

    but when i use round php function i got round but i dont need round, i just need to delete all digits after 2 decimal simple.

    i have tired, flot() and lot of other option no success.

    Thanks

  • Philipp Kyeck
    Philipp Kyeck over 11 years
    it should maybe be mentioned that number_format returns a string and not a number.
  • Justin
    Justin over 10 years
    This is the simplest method. number_format still rounds the result.
  • Sepster
    Sepster over 8 years
    This is rounds, then truncates. So if $var == 2.509 then the above will return 2.51, not 2.50. So does not answer the question that explicitly states "not round".
  • Sepster
    Sepster over 8 years
    This is rounds, then truncates. So if the input number was 2.509 then the above will return 2.51, not 2.50. So does not answer the question that explicitly states "not round"
  • Sepster
    Sepster over 8 years
    sprintf does round, though. sprintf("%01.2f", 2.509) != '2.50'. -1 sorry.
  • Sepster
    Sepster over 8 years
    This should be marked as the answer IMHO, @air. All other answers round prior to truncation, such that the rounding is only evident when it results in increment of the last desired (in this case, 2nd) decimal place. This solution avoids that by effectively moving the decimal point two places right (by multiplying by 100), truncating (using floor), then moving it back 2 places left (divide by 100). Genius in its simplicity.
  • Sepster
    Sepster over 8 years
    An explanation as to why floor(205) is returning 204 here, can be found at php.net/manual/en/function.floor.php#114204
  • random_user_name
    random_user_name over 8 years
    @Sepster - actually, this has an issue with rounding negative numbers. I've improved my answer to address even that scenario.
  • random_user_name
    random_user_name over 8 years
    This actually will round negative numbers.
  • Sepster
    Sepster over 8 years
    Good pickup, @cale_b!
  • Sepster
    Sepster over 8 years
    -1 removed. No +1 though as still not quite right - try this for 2.05. :-) You need to factor in an epsilon to account for float storage irregularities - refer @David Constantine's answer (and now, mine!).
  • William Rossier
    William Rossier about 8 years
    I used this and it's works: bcdiv($price,1,2); But this one too: floor(2.56789 * 100) / 100; // 2.56
  • random_user_name
    random_user_name about 8 years
    @WilliamRossier - bcdiv is the right answer. floor has a problem with negative numbers, so is not correct. Nice tip!
  • random_user_name
    random_user_name almost 8 years
    @Reado - can you expand / clarify how bcdiv is incorrect? I'd like to know / correct the answer.
  • Muhammad Atif
    Muhammad Atif over 7 years
    BEWARE! a zero should not be passed to truncate_number as $number
  • random_user_name
    random_user_name over 7 years
    @MuhammadAtif - yes, that's not the correct answer in this post. The correct answer is bcdiv
  • GrumpyCrouton
    GrumpyCrouton over 7 years
    This could also very easily be turned into a function where you can input the amount of decimals you'd like to see as well.
  • Андрей Аршинов
    Андрей Аршинов about 7 years
    By the way, bcadd($num_FLO, 0, $prec_INT) is preffered if you only need to delete digits because it's working faster that div or mul operations)
  • Bill_VA
    Bill_VA over 6 years
    So close. This: if ( 0 == (int)$number ) { return $number; } Should be if ( (int)$number == 0 ) { return $number; } Just a bit backwards. Thanks for the function.
  • random_user_name
    random_user_name over 6 years
    @Bill_VA - thanks for the comment. Actually, my code is correct (as is yours), and mine follows a "defensive" coding pattern known Yoda Conditions. If you forget one of the = symbols in your code, you get an unintentional assignment of 0 to the $number variable. If you forget one of the = symbols in my code, you get an error, and the PHP won't run.
  • Jimmy Adaro
    Jimmy Adaro about 6 years
    With bcdiv(0.20198302, 1, 2) keep in mind this: 0.20 will be shown as 0.2.
  • random_user_name
    random_user_name about 6 years
    @JimmyAdaro - this is not about formatting a guaranteed number of decimal places, this is about truncating to a maximum number of decimal places, without rounding. Therefore, your comment is "correct / expected" behavior.
  • FantomX1
    FantomX1 about 6 years
    doesn't work on numbers which are periodic in binary system, try it, for example 'intval(2.092484 * 1000000) / 1000000' => you get '2.092483' , however yours string minupulation with preg_replace works very well, big up from me, thank you
  • FantomX1
    FantomX1 about 6 years
    floor doesn't work on numbers which are periodic in binary system, try it, for example 'floor(2.092484 * 1000000) / 1000000' => you get '2.092483'
  • random_user_name
    random_user_name about 6 years
    @FantomX1 - Floor is not the answer. It is included in my post only to demonstrate why it is not the answer. bcdiv, which is at the top and the bottom of my post, is the correct answer.
  • flu
    flu about 6 years
    @FantomX1 You're absolutely right. If I wanted to be a nitpicker I'd say that the OP asked for two digits and 'intval(2.092484 * 100) / 100' is 2.09` but I think using round instead of intval should make this more bulletproof. Thanks!
  • FantomX1
    FantomX1 about 6 years
    good point with two decimals, me personally needed 6 though, round removes periodic number infinite sequence that's true, but you don't always want to round like for example me when I compare data against remote system, where mysql column does exactly what your string replace, just cuts off from a specific ordinal digit I want to have always returned 2.09 no matter if its 2.094 or 2.096, therefore round is of no use for me, but preg replace works
  • nights
    nights over 5 years
    Also he asked how to delete, you are still keeping the fraction
  • Sol
    Sol almost 5 years
    Absolute the best answer on the net so far typeof() says it is a double too!
  • Adarsh Mohan
    Adarsh Mohan almost 5 years
    This wont work with "2.500000550" as the value. You will get "2.50000055" as the result as per your solution.
  • Fahad Ahmed
    Fahad Ahmed almost 5 years
    You have to increase the length, substr(2.500000550, 0, -6); then you will get '2.50',
  • Adarsh Mohan
    Adarsh Mohan almost 5 years
    The data comes from a mysql database, so that should be dynamic
  • victoryoalli
    victoryoalli over 2 years
    Just tried it it is giving me, which seems correct. truncate_decimals(10056468765.254564) => 10056468765.25
  • JGCW
    JGCW over 2 years
    I'll investigate and I'll delete my previous comment. If I find steps to reproduce, ill post them here.
  • mickmackusa
    mickmackusa almost 2 years
    How about preg_replace('/\.\d{2}\K\d+', '', $var) instead?