71

Is there a straightforward way of determining the number of decimal places in a(n) integer/double value in PHP? (that is, without using explode)

Machavity
  • 30,841
  • 27
  • 92
  • 100
Erwin
  • 1,762
  • 3
  • 24
  • 30

18 Answers18

117
$str = "1.23444";
print strlen(substr(strrchr($str, "."), 1));
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 11
    This wouldn't work on extremely large or extremely small floats that get converted to scientific notation when represented as a string. – GordonM Feb 11 '15 at 13:19
  • 6
    An example: `var_dump((string)(0.00000000000012));` outputs `string(7) "1.2E-13"` – ThW Feb 26 '15 at 10:40
  • 6
    @GordonM You can get around that with number_format: `$str = rtrim(number_format($value, 14 - log10($value)), '0');`. 14 is based on the maximum precision of floats in PHP; it may need to be reduced a bit if you're doing some math on the float first (especially if it was obtained by subtracting two larger numbers). – Brilliand Jun 12 '15 at 23:02
  • 4
    If using with a lot of numbers, it may be more efficient to just substract 1, instead of getting the substring: strlen(strrchr($str, "."))-1; – vicenteherrera Dec 30 '15 at 10:36
  • @GordonM Your solution is genius. But is depending on php not changing the precission sometime in the future or on rather unusual systems – Hafenkranich Jan 23 '18 at 06:47
  • @Brilliand This solution has a problem that the TypeError: number_format() expects parameter 2 to be integer, float given occurs. – Lunack Apr 05 '18 at 08:00
  • @VijaySankhat Actually it works perfectly since you do have 2 decimals. If you'd like to do some kind of "rounding" on a string, then you can try something like `strlen(substr(strrchr(rtrim($str, "0"), "."), 1));` – Coldark Aug 13 '19 at 13:21
22

Less code:

$str = "1.1234567";
echo (int) strpos(strrev($str), ".");
M Rostami
  • 4,035
  • 1
  • 35
  • 39
14

You could try casting it to an int, subtracting that from your number and then counting what's left.

Allyn
  • 20,271
  • 16
  • 57
  • 68
  • 3
    Very good answear in my opinion, if I am right you must mean this: `echo strlen($num-(int)$num)-2;` – Melsi Feb 25 '12 at 16:06
  • 2
    `$decimals = ( (int) $num != $num ) ? (strlen($num) - strpos($num, '.')) - 1 : 0; ` works with negative numbers too. – Evan Mattson Apr 14 '13 at 01:22
  • @EvanMattson that will not work for very large floats that overflow. – APorter1031 Feb 23 '21 at 14:26
  • 1
    I was doing this in Dart, and this doesn't work because floating point subtraction is not precise, e.g. doing `(3.005 - 3).toString().length` got me 20. I expect this behaviour in any programming language? – Ben Butterworth Mar 05 '21 at 19:15
13
function numberOfDecimals($value)
{
    if ((int)$value == $value)
    {
        return 0;
    }
    else if (! is_numeric($value))
    {
        // throw new Exception('numberOfDecimals: ' . $value . ' is not a number!');
        return false;
    }

    return strlen($value) - strrpos($value, '.') - 1;
}


/* test and proof */

function test($value)
{
    printf("Testing [%s] : %d decimals\n", $value, numberOfDecimals($value));
}

foreach(array(1, 1.1, 1.22, 123.456, 0, 1.0, '1.0', 'not a number') as $value)
{
    test($value);
}

Outputs:

Testing [1] : 0 decimals
Testing [1.1] : 1 decimals
Testing [1.22] : 2 decimals
Testing [123.456] : 3 decimals
Testing [0] : 0 decimals
Testing [1] : 0 decimals
Testing [1.0] : 0 decimals
Testing [not a number] : 0 decimals
Kris
  • 40,604
  • 9
  • 72
  • 101
  • 2
    note that php interprets a literal 1.0 in source as integer so when converted to string it does not have decimals. (even if you cast it to float at declaration so `$variable = (float)1.0;` ***does not work***) – Kris Mar 12 '10 at 03:06
  • 3
    This wouldn't work with very small or very big floats, which are output as scientific notation when cast to string. var_dump (strval (1/1000000)); – GordonM Feb 11 '15 at 13:22
  • 1
    This does not work in an international context, but with a little change it will: `$localconv = localeconv(); return strlen($value) - strrpos($value, $localconv['decimal_point']) - 1;` – Jasper Briers Dec 16 '21 at 15:49
5

I needed a solution that works with various number formats and came up with the following algorithms:

// Count the number of decimal places
$current = $value - floor($value);
for ($decimals = 0; ceil($current); $decimals++) {
    $current = ($value * pow(10, $decimals + 1)) - floor($value * pow(10, $decimals + 1));
}

// Count the total number of digits (includes decimal places)
$current = floor($value);
for ($digits = $decimals; $current; $digits++) {
    $current = floor($current / 10);
}

Results:

input:    1
decimals: 0
digits:   1

input:    100
decimals: 0
digits:   3

input:    0.04
decimals: 2
digits:   2

input:    10.004
decimals: 3
digits:   5

input:    10.0000001
decimals: 7
digits:   9

input:    1.2000000992884E-10
decimals: 24
digits:   24

input:    1.2000000992884e6
decimals: 7
digits:   14
Jeremy Worboys
  • 533
  • 3
  • 16
3

I used the following to determine whether a returned value has any decimals (actual decimal values, not just formatted to display decimals like 100.00):

if($mynum - floor($mynum)>0) {has decimals;} else {no decimals;} 
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
skips
  • 31
  • 2
3
<?php

test(0);
test(1);
test(1.234567890);
test(-123.14);
test(1234567890);
test(12345.67890);

function test($f) {
    echo "f = $f\n";
    echo "i = ".getIntCount($f)."\n";
    echo "d = ".getDecCount($f)."\n";
    echo "\n";
}

function getIntCount($f) {
    if ($f === 0) {
        return 1;
    } elseif ($f < 0) {
        return getIntCount(-$f);
    } else {
        return floor(log10(floor($f))) + 1;
    }
}

function getDecCount($f) {
    $num = 0;
    while (true) {
        if ((string)$f === (string)round($f)) {
            break;
        }
        if (is_infinite($f)) {
            break;
        }

        $f *= 10;
        $num++;
    }
    return $num;
}

Outputs:

f = 0
i = 1
d = 0

f = 1
i = 1
d = 0

f = 1.23456789
i = 1
d = 8

f = -123.14
i = 3
d = 2

f = 1234567890
i = 10
d = 0

f = 12345.6789
i = 5
d = 4
Sergey
  • 351
  • 3
  • 11
  • This was the only solution I get. Test: https://phpsandbox.io/e/x/73eup?layout=EditorPreview&defaultPath=%2F&theme=dark&showExplorer=no&openedFiles= – alvaro.canepa Mar 07 '23 at 18:22
2

Here's a function that takes into account trailing zeroes:

function get_precision($value) {
    if (!is_numeric($value)) { return false; }
    $decimal = $value - floor($value); //get the decimal portion of the number
    if ($decimal == 0) { return 0; } //if it's a whole number
    $precision = strlen($decimal) - 2; //-2 to account for "0."
    return $precision; 
}
RJR
  • 99
  • 5
  • This was great, but there's a shorter way to do it: is_numeric($value) ? max(strlen(strrchr(abs($value), ".")) - 1, 0) : FALSE; – Giles B Oct 25 '17 at 13:54
2

Something like:

<?php

$floatNum = "120.340304";
$length = strlen($floatNum);

$pos = strpos($floatNum, "."); // zero-based counting.

$num_of_dec_places = ($length - $pos) - 1; // -1 to compensate for the zero-based count in strpos()

?>

This is procedural, kludgy and I wouldn't advise using it in production code. But it should get you started.

David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • I tried changing the sample value to an integer value, and the result says "2". What I'm trying to achieve is a process that will return 0 if the number is an integer, else the number of decimal places – Erwin Mar 12 '10 at 02:36
  • 1
    When you say 'integer' did your integer have the form '10.00' or '10'? – David Thomas Mar 12 '10 at 02:42
  • Its value is 10. I'm expecting different kind of format such as 10, 10.00, 10.000. Based on the format, I will have to know the number of decimal digits used – Erwin Mar 12 '10 at 02:44
2

Solution

$num = "12.1234555";
print strlen(preg_replace("/.*\./", "", $num)); // 7

Explanation

Pattern .*\. means all the characters before the decimal point with its.

In this case it's string with three characters: 12.

preg_replace function converts these cached characters to an empty string "" (second parameter).

In this case we get this string: 1234555

strlen function counts the number of characters in the retained string.

simhumileco
  • 31,877
  • 16
  • 137
  • 115
  • 3
    Perhaps this answer could be improved by explaining your code for OP – WillardSolutions Dec 23 '16 at 22:27
  • 1
    This use case will not handle values without decimals. This is correct implementation limited replace (to turn off global replace): `strlen(preg_replace('/[\d]+[\.]?/', '', $finalValue, 1))` – Pion Mar 01 '18 at 09:24
2

If you want readability for the benefit of other devs, locale safe, use:

function countDecimalPlacesUsingStrrpos($stringValue){
    $locale_info = localeconv();
    $pos = strrpos($stringValue, $locale_info['decimal_point']);
    if ($pos !== false) {
        return strlen($stringValue) - ($pos + 1);
    }
    return 0;
}

see localeconv

Phil Hilton
  • 185
  • 1
  • 2
  • 8
Ian
  • 2,898
  • 1
  • 27
  • 29
  • I had no idea I did that. I approved it. Later that day I looked at this and found the edit not to have applied, so I changed it myself. No idea what happened. – Ian Sep 25 '18 at 20:50
  • 1
    Well it's still not there so I'm going to resubmit it. The function does not work as is on floats that do not have a leading digit such as .25 or .3333 . (vs 0.25 or 0.3333). This is because $pos is 0 which evaluates to false. In this case, 0 is exactly the right answer, so we need to do a a type savvy check for $pos !== false instead. strrpos will return false if the decimal character is not found at all, but 0 if it is the first character in the string. – Phil Hilton Sep 26 '18 at 02:52
  • Thanks Phil, I ran some unit tests and it now handles values without a preceding number, E.g. .34 – Ian Sep 26 '18 at 10:45
1
$decnumber = strlen(strstr($yourstr,'.'))-1
Young
  • 7,986
  • 7
  • 43
  • 64
1

Int

Integers do not have decimal digits, so the answer is always zero.

Double/Float

Double or float numbers are approximations. So they do not have a defined count of decimal digits.

A small example:

$number = 12.00000000012;
$frac = $number - (int)$number;

var_dump($number);
var_dump($frac);

Output:

float(12.00000000012)
float(1.2000000992884E-10)

You can see two problems here, the second number is using the scientific representation and it is not exactly 1.2E-10.

String

For a string that contains a integer/float you can search for the decimal point:

$string = '12.00000000012';
$delimiterPosition = strrpos($string, '.');
var_dump(
  $delimiterPosition === FALSE ? 0 : strlen($string) - 1 - $delimiterPosition
);

Output:

int(11)
ThW
  • 19,120
  • 3
  • 22
  • 44
0

How about this?:

$iDecimals = strlen($sFull%1);
Anton
  • 1,029
  • 7
  • 19
0

First I have found the location of the decimal using strpos function and increment the strpos postion value by 1 to skip the decimal place.

Second I have subtracted the whole string length from the value I have got from the point1.

Third I have used substr function to get all digits after the decimal.

Fourth I have used the strlen function to get length of the string after the decimal place.

This is the code that performs the steps described above:

     <?php
        $str="98.6754332";
        echo $str;
        echo "<br/>";
        echo substr( $str, -(strlen($str)-(strpos($str, '.')+1)) );
        echo "<br/>";
        echo strlen( substr( $str, -(strlen($str)-(strpos($str, '.')+1))) );
    ?>
Stibu
  • 15,166
  • 6
  • 57
  • 71
rahul sharma
  • 111
  • 4
  • It would be helpful if you could explain how your solution works. – Stibu Dec 29 '15 at 10:48
  • @Stibu hi..i have updated the answer please check and let me know if you have any concern. – rahul sharma Dec 29 '15 at 12:22
  • You added some text, which is definitely a good idea. Since I'm not familiar with PHP, I can not judge the contents of your post. I have tried to clean up the formatting and reworded your last sentence. If you do not agree with the changes, feel free to remove them or edit again. – Stibu Dec 29 '15 at 12:49
0

This will work for any numbers, even in scientific notation, with precision up to 100 decimal places.

$float = 0.0000005;

$working = number_format($float,100);
$working = rtrim($working,"0");
$working = explode(".",$working);
$working = $working[1];

$decmial_places = strlen($working);

Result:

7

Lengthy but works without complex conditionals.

McClint
  • 101
  • 5
  • 1
    Actually, it doesn't work at all. [demo](http://sandbox.onlinephpfunctions.com/code/a190385ce2c93a596108800c3d1ad4cefb5a493d) – Coldark Aug 13 '19 at 13:10
-1
$value = 182.949;

$count = strlen(abs($value - floor($value))) -2; //0.949 minus 2 places (0.)
Ivan Aksamentov - Drop
  • 12,860
  • 3
  • 34
  • 61
Leonardo Jorge
  • 177
  • 1
  • 3
-1

You should always be careful about different locales. European locales use a comma for the thousands separator, so the accepted answer would not work. See below for a revised solution:

 function countDecimalsUsingStrchr($stringValue){
        $locale_info = localeconv();
        return strlen(substr(strrchr($stringValue, $locale_info['decimal_point']), 1));
    }

see localeconv

Ian
  • 2,898
  • 1
  • 27
  • 29