38

I have this number:

$double = '21.188624';

After using number_format($double, 2, ',', ' ') I get:

21,19

But what I want is:

21,18

Any ideea how can I make this work?

Thank you.

T30
  • 11,422
  • 7
  • 53
  • 57
Psyche
  • 8,513
  • 20
  • 70
  • 85
  • 2
    You can simply subtract 5 * 10 ^ - ( 1 + decimal places). So `number_format($double - 0.005, 2, ',', ' ')` – Pete Jun 05 '18 at 15:40

18 Answers18

38

number_format will always do that, your only solution is to feed it something different:

$number = intval(($number*100))/100;

Or:

$number = floor(($number*100))/100;
Wrikken
  • 69,272
  • 8
  • 97
  • 136
  • 6
    Try this with this number : 0.29 , and it fails! – Arsham Aug 22 '13 at 10:59
  • do this: $finalCommishParts = explode('.',$commission); $commisshSuffix = (isset($finalCommishParts[1])?substr($finalCommishParts[1],0,2):'00'); $finalCommish = $finalCommishParts[0].'.'.$commisshSuffix; – Asaf Maoz May 15 '14 at 12:37
  • 1
    @AsafMaoz: beware ot decimal separator differences with locales. – Wrikken May 16 '14 at 00:03
  • 1
    @Arsham: that's because 0.29 does not exist as a float: `printf('%.20f',0.29)` shows you: `0.28999999999999998002`, so, it correctly forces the round down. – Wrikken May 16 '14 at 00:07
  • 1
    @AsafMaoz: You could use str_pad($string, 2,'0',STR_PAD_RIGHT) instead of the substring method too BTW. – Wrikken May 16 '14 at 00:10
  • @Wrikken ,didn't need to check for decimal separator differences in my case, but sure, use it as a var and change from ',' to '.' as needed. – Asaf Maoz May 18 '14 at 08:46
  • For doubles that don't round up and prove it with gettype() https://stackoverflow.com/questions/9944001/delete-digits-after-two-decimal-points-without-rounding-the-value/9944052#9944052 – Sol May 21 '19 at 06:00
  • use round buddy, use round! – TheFaultInOurStars Jun 08 '20 at 22:32
20

I know that this an old question, but it still actual :) .

How about this function?

function numberFormatPrecision($number, $precision = 2, $separator = '.')
{
    $numberParts = explode($separator, $number);
    $response = $numberParts[0];
    if (count($numberParts)>1 && $precision > 0) {
        $response .= $separator;
        $response .= substr($numberParts[1], 0, $precision);
    }
    return $response;
}

Usage:

// numbers test
numberFormatPrecision(19, 2, '.'); // expected 19 return 19
numberFormatPrecision(19.1, 2, '.'); //expected 19.1 return 19.1
numberFormatPrecision(19.123456, 2, '.'); //expected 19.12 return 19.12
numberFormatPrecision(19.123456, 0, '.'); //expected 19 return 19

// negative numbers test
numberFormatPrecision(-19, 2, '.'); // expected -19 return -19
numberFormatPrecision(-19.1, 2, '.'); //expected -19.1 return -19.1
numberFormatPrecision(-19.123456, 2, '.'); //expected -19.12 return -19.12
numberFormatPrecision(-19.123456, 0, '.'); //expected -19 return -19

// precision test
numberFormatPrecision(-19.123456, 4, '.'); //expected -19.1234 return -19.1234

// separator test
numberFormatPrecision('-19,123456', 3, ','); //expected -19,123 return -19,123  -- comma separator
ustmaestro
  • 1,233
  • 12
  • 20
16

Function (only precision):

function numberPrecision($number, $decimals = 0)
{
    $negation = ($number < 0) ? (-1) : 1;
    $coefficient = 10 ** $decimals;
    return $negation * floor((string)(abs($number) * $coefficient)) / $coefficient;
}

Examples:

numberPrecision(2557.9999, 2);     // returns 2557.99
numberPrecision(2557.9999, 10);    // returns 2557.9999
numberPrecision(2557.9999, 0);     // returns 2557
numberPrecision(2557.9999, -2);    // returns 2500
numberPrecision(2557.9999, -10);   // returns 0
numberPrecision(-2557.9999, 2);    // returns -2557.99
numberPrecision(-2557.9999, 10);   // returns -2557.9999
numberPrecision(-2557.9999, 0);    // returns -2557
numberPrecision(-2557.9999, -2);   // returns -2500
numberPrecision(-2557.9999, -10);  // returns 0

Function (full functionality):

function numberFormat($number, $decimals = 0, $decPoint = '.' , $thousandsSep = ',')
{
    $negation = ($number < 0) ? (-1) : 1;
    $coefficient = 10 ** $decimals;
    $number = $negation * floor((string)(abs($number) * $coefficient)) / $coefficient;
    return number_format($number, $decimals, $decPoint, $thousandsSep);
}

Examples:

numberFormat(2557.9999, 2, ',', ' ');     // returns 2 557,99
numberFormat(2557.9999, 10, ',', ' ');    // returns 2 557,9999000000
numberFormat(2557.9999, 0, ',', ' ');     // returns 2 557
numberFormat(2557.9999, -2, ',', ' ');    // returns 2 500
numberFormat(2557.9999, -10, ',', ' ');   // returns 0
numberFormat(-2557.9999, 2, ',', ' ');    // returns -2 557,99
numberFormat(-2557.9999, 10, ',', ' ');   // returns -2 557,9999000000
numberFormat(-2557.9999, 0, ',', ' ');    // returns -2 557
numberFormat(-2557.9999, -2, ',', ' ');   // returns -2 500
numberFormat(-2557.9999, -10, ',', ' ');  // returns 0
Dima
  • 311
  • 2
  • 5
7
floor($double*100)/100
methodin
  • 6,717
  • 1
  • 25
  • 27
5

I use this function:

function cutNum($num, $precision = 2) {
    return floor($num) . substr(str_replace(floor($num), '', $num), 0, $precision + 1);
}

Usage examples:

cutNum(5)          //returns 5 
cutNum(5.6789)     //returns 5.67 (default precision is two decimals)
cutNum(5.6789, 3)  //returns 5.678
cutNum(5.6789, 10) //returns 5.6789
cutNum(5.6789, 0)  //returns 5. (!don't use with zero as second argument: use floor instead!)

Explanation: here you have the same function, just more verbose to help understanding its behaviour:

function cutNum($num, $precision = 2) {
    $integerPart = floor($num);
    $decimalPart = str_replace($integerPart, '', $num);
    $trimmedDecimal = substr($decimalPart, 0, $precision + 1);
    return $integerPart . $trimmedDecimal;
}
T30
  • 11,422
  • 7
  • 53
  • 57
  • You saved my time Thanks – Tejas Mehta Dec 20 '17 at 10:25
  • test cutNum(510.9) // return 510.89 – ustmaestro Feb 18 '19 at 13:43
  • Thanks @ustmaestro, it's due to a Php [floating conversion issue](https://stackoverflow.com/q/17210787/1677209). I've replaced the floating calculation with string replace function, as a workaround for this scenarios. – T30 Feb 18 '19 at 16:50
  • 2
    Your function isn't correct. 99.997092 will return 99.70 when it should return 99.99. Here is the proper function. https://pastebin.com/6i9X536G – OwN Apr 19 '19 at 16:39
  • I loved it at first until I used gettype() and found it to be a string and not really a double – Sol May 21 '19 at 05:52
  • This is the best one found so far for doubles https://stackoverflow.com/questions/9944001/delete-digits-after-two-decimal-points-without-rounding-the-value/9944052#9944052 – Sol May 21 '19 at 06:02
  • 52.3499999999999 returns 52.35 – Alexander M. Jul 23 '20 at 22:36
4

Use the PHP native function bcdiv.

function numberFormat($number, $decimals = 2, $sep = ".", $k = ","){
    $number = bcdiv($number, 1, $decimals); // Truncate decimals without rounding
    return number_format($number, $decimals, $sep, $k); // Format the number
}

See this answer for more details.

Etienne Martin
  • 10,018
  • 3
  • 35
  • 47
  • This solution is very nice but you need to feed strings to bcdiv otherwise it will print 0.00000 if you try to numberFormat small numbers (I tested it and it does not work with 0.00001). Solution: $number = bcdiv(number_format($number, 30, '.', ''), '1', $decimals); – Gotenks Dec 06 '18 at 12:00
2
 **Number without round**        

   $double = '21.188624';
   echo intval($double).'.'.substr(end(explode('.',$double)),0,2);

**Output** 21.18
Abhishek Sharma
  • 300
  • 2
  • 7
1

In case you don't care for what comes behind the decimal point, you can cast the float as an int to avoid rounding:

$float = 2.8;
echo (int) $float; // outputs '2'
kasimir
  • 1,506
  • 1
  • 20
  • 26
1
$double = '21.188624';

$teX = explode('.', $double);

if(isset($teX[1])){
    $de = substr($teX[1], 0, 2);
    $final = $teX[0].'.'.$de;
    $final = (float) $final;
}else{
    $final = $double;   
}

final will be 21.18

Bill Stephen
  • 99
  • 1
  • 4
1

In case you need 2 fixed decimal places, you can try this!

@Dima's solution is working for me, but it prints "19.90" as "19.9" so I made some changes as follows:

<?php 
function numberPrecision($number, $decimals = 0)
    {
        $negation = ($number < 0) ? (-1) : 1;
        $coefficient = 10 ** $decimals;
        $result = $negation * floor((string)(abs($number) * $coefficient)) / $coefficient;
        $arr = explode(".", $result);
        $num = $arr[0];
        if(empty($arr[1]))
            $num .= ".00";
        else if(strlen($arr[1]) == 1)
            $num .= "." . $arr[1] . "0";
        else
            $num .= ".". $arr[1];
        return $num;
    }
    echo numberPrecision(19.90,2); // 19.90

So, what I did is, I just break the result into two parts with explode function. and convert the result into a string with concatenation!

Sourav Dutt
  • 70
  • 1
  • 8
0
$finalCommishParts = explode('.',$commission);
$commisshSuffix = (isset($finalCommishParts[1])?substr($finalCommishParts[1],0,2):'00');
$finalCommish = $finalCommishParts[0].'.'.$commisshSuffix;
Matt
  • 74,352
  • 26
  • 153
  • 180
Asaf Maoz
  • 675
  • 3
  • 6
  • 23
  • 2
    Please add explaining text. I don't see `number_format(...)` in this answer, so it is beyond me how this answers the question "How to make number_format() not to round numbers up" – Sumurai8 Jul 19 '14 at 17:48
  • This snippet does what number_format does, but without rounding up the number. As mentioned above, check for your local decimal separator. – Asaf Maoz Jul 20 '14 at 11:44
0
public function numberFormatPrecision( $number, $separator = '.', $format = 2 ){

    $response = '';
    $brokenNumber = explode( $separator, $number );
    $response = $brokenNumber[0] . $separator;
    $brokenBackNumber = str_split($brokenNumber[1]);

    if( $format < count($brokenBackNumber) ){

        for( $i = 1; $i <= $format; $i++ )
            $response .= $brokenBackNumber[$i];
    }

    return $response;
}
Keith Yeoh
  • 731
  • 1
  • 7
  • 20
  • You cannot have for( $i = 1;... because $brokenBackNumber[$i] will then miss the first character - since $brokenBackNumber is zero based. You should also have an 'else{}' section for your if statement. But I like your idea. – Gerhard Liebenberg Mar 23 '15 at 17:39
0

The faster way as exploding(building arrays) is to do it with string commands like this:

$number = ABC.EDFG;
$precision = substr($number, strpos($number, '.'), 3); // 3 because . plus 2 precision  
$new_number = substr($number, 0, strpos($number, '.')).$precision;

The result ist ABC.ED in this case because of 2 precision If you want more precision just change the 3 to 4 or X to have X-1 precision

Cheers

quinlan
  • 112
  • 1
  • 7
0

Javascript Version

function numberFormat($number, $decimals = 0, $decPoint = '.' , $thousandsSep = ',')
{
    return number_format((Math.floor($number * 100) / 100).toFixed($decimals), $decimals, $decPoint, $thousandsSep );
}
 // https://locutus.io/php/strings/number_format/
function number_format(number, decimals, decPoint, thousandsSep) {
    if(decimals === 'undefined') decimals = 2;

    number = (number + '').replace(/[^0-9+\-Ee.]/g, '')
  const n = !isFinite(+number) ? 0 : +number
  const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
  const sep = (typeof thousandsSep === 'undefined') ? ',' : thousandsSep
  const dec = (typeof decPoint === 'undefined') ? '.' : decPoint
  let s = ''
  const toFixedFix = function (n, prec) {
    if (('' + n).indexOf('e') === -1) {
      return +(Math.round(n + 'e+' + prec) + 'e-' + prec)
    } else {
      const arr = ('' + n).split('e')
      let sig = ''
      if (+arr[1] + prec > 0) {
        sig = '+'
      }
      return (+(Math.round(+arr[0] + 'e' + sig + (+arr[1] + prec)) + 'e-' + prec)).toFixed(prec)
    }
  }
  // @todo: for IE parseFloat(0.55).toFixed(0) = 0;
  s = (prec ? toFixedFix(n, prec).toString() : '' + Math.round(n)).split('.')
  if (s[0].length > 3) {
    s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep)
  }
  if ((s[1] || '').length < prec) {
    s[1] = s[1] || ''
    s[1] += new Array(prec - s[1].length + 1).join('0')
  }
  return s.join(dec)
}
mean.cj
  • 113
  • 7
0
    $number = 2.278;
    echo new_number_format($number,1);
    //result: 2.2

    function new_number_format($number,$decimal)
    {
        //explode the number with the delimiter of dot(.) and get the whole number in index 0 and the decimal in index 1
        $num = explode('.',$number);
        //if the decimal is equal to zero
        //take note that we can't split the zero value only and it will return Undefined offset if we split the zero only
        //for example: rating_format(2.0,1); the result will be 2. the zero is gone because of the Undefined offset
        //the solution of this problem is this condition below
        if($num[1] == 0)
        {
          $final_decimal = '';
          $i=0;
          //loop the decimal so that we can display depend on how many decimal that you want to display
          while($i<$decimal){
            $final_decimal .= 0;
            $i++;
          }
        }
        //if the decimal is not zero
        else
        {
          $dec = str_split($num[1]); //split the decimal and get the value using the array index
          $i=0;
          $final_decimal = '';
          //loop the decimal so that we can display depend on how many decimal that you want to display
          while($i<$decimal){
            $final_decimal .= $dec[$i];
            $i++;
            }
        }
          $new_number= $num[0].'.'.$final_decimal;//combine the result with final decimal
          return $new_number; //return the final output
    }
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 04 '23 at 09:01
0

thanks for your help Dima!!! My function

    private function number_format(float $num, int $decimals = 0, ?string $decimal_separator = ',', ?string $thousands_separator = '.'){
     /**
      * Formatea un numero como number_format sin redondear hacia arriba, trunca el resultado
     * @access private
     * @param string num - Numero de va a ser formateado
     * @param int decimals - Posiciones Decimales
     * @param string|null $decimal_separator — [opcional]
     * @param string|null $thousands_separator — [opcional]
     * @return string — Version de numero formateado.
     */

     $negation = ($num < 0) ? (-1) : 1;
     $coefficient = 10 ** $decimals;
     $number = $negation * floor((string)(abs($num) * $coefficient)) / $coefficient;
     return number_format($number, $decimals, $decimal_separator, $thousands_separator);
}

for use it

echo $this->number_format(24996.46783, 3, ',', '.'); //24.996,467
-1

use this function:

function number_format_unlimited_precision($number,$decimal = '.')
{
   $broken_number = explode($decimal,$number);
   return number_format($broken_number[0]).$decimal.$broken_number[1]);
}
Linus Kleen
  • 33,871
  • 11
  • 91
  • 99
rahim asgari
  • 12,197
  • 10
  • 43
  • 53
  • you have an extra ) $broken_number[1]); – ddjikic Aug 08 '13 at 20:24
  • 1
    This does nothing: simply returns the same input number as it's passed (also there is bracket mispelling). – T30 Apr 28 '15 at 14:36
  • I know it's an old response and it's not a really good answer, but let's get clear that there's an edit, and if you look at the history, the extra bracket was added by this Linus dude and not by the original answerer. – Joao Paulo Rabelo Mar 08 '17 at 20:59
-1

In Case you have small float values you can use number_format function this way.

$number = 21.23;

echo number_format($number, 2, '.', ',') ); // 21.23

In case you have you have long decimal number then also it will format number this way

$number = 201541.23;

echo number_format($number, 2, '.', ',') ); // 201,541.23
Vel
  • 9,027
  • 6
  • 34
  • 66
Mayank Majithia
  • 1,916
  • 16
  • 21