57

I have a set of string numbers having decimals, for example: 23.456, 9.450, 123.01... I need to retrieve the number of decimals for each number, knowing that they have at least 1 decimal.

In other words, the retr_dec() method should return the following:

retr_dec("23.456") -> 3
retr_dec("9.450")  -> 3
retr_dec("123.01") -> 2

Trailing zeros do count as a decimal in this case, unlike in this related question.

Is there an easy/delivered method to achieve this in Javascript or should I compute the decimal point position and compute the difference with the string length? Thanks

Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453

12 Answers12

117
function decimalPlaces(num) {
  var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
  if (!match) { return 0; }
  return Math.max(
       0,
       // Number of digits right of decimal point.
       (match[1] ? match[1].length : 0)
       // Adjust for scientific notation.
       - (match[2] ? +match[2] : 0));
}

The extra complexity is to handle scientific notation so

decimalPlaces('.05')
2
decimalPlaces('.5')
1
decimalPlaces('1')
0
decimalPlaces('25e-100')
100
decimalPlaces('2.5e-99')
100
decimalPlaces('.5e1')
0
decimalPlaces('.25e1')
1
Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
  • Note that the OP has already stated that it is not a number, but a string with a number inside. – Phrogz May 04 '12 at 18:55
  • 1
    @Phrogz, I don't think that affects the validity of my answer, except that maybe I shouldn't count 0's on the end of the \.(\d+) – Mike Samuel May 04 '12 at 18:57
  • It doesn't, I just wanted to point out that the `''+` was unnecessary in this case. – Phrogz May 04 '12 at 19:28
  • 1.00 returns 0 :(, instead of 2 – arod Oct 21 '15 at 03:06
  • 2
    @arod, `decimalPlaces("1.00") === 2` but `decimalPlaces(1.00) === 0` because `("" + 1.00) === "1"`. The function operates on string representations of numbers, not primitive numeric values because primitive numeric values aren't inherently decimal -- [IEEE 854 didn't make it into EcmaScript 5](http://intertwingly.net/blog/2008/07/11/Decimal-in-ECMAScript). – Mike Samuel Oct 21 '15 at 13:14
  • I wish I had more upvotes to give this lovely function. – russellmania Jul 13 '16 at 19:10
  • Isn't this a dupe of [this other question you've answered](https://stackoverflow.com/questions/9539513/is-there-a-reliable-way-in-javascript-to-obtain-the-number-of-decimal-places-of)? – Dan Dascalescu Jun 14 '17 at 08:25
  • @DanDascalescu, seems like it. I'm always surprised when I find that I disagree with myself. – Mike Samuel Mar 02 '18 at 17:04
59
function retr_dec(num) {
  return (num.split('.')[1] || []).length;
}
Community
  • 1
  • 1
Jack
  • 9,448
  • 3
  • 29
  • 33
10
function retr_dec(numStr) {
    var pieces = numStr.split(".");
    return pieces[1].length;
}
Daniel Bidulock
  • 2,344
  • 1
  • 26
  • 27
5

Since there is not already a regex-based answer:

/\d*$/.exec(strNum)[0].length

Note that this "fails" for integers, but per the problem specification they will never occur.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • 5
    @LarryBattle I appreciate your comment and thoroughness, but the question clearly states, _"knowing that they have at least 1 decimal"_. If you down voted my correct answer for this reason, I ask you to reconsider your vote. – Phrogz May 16 '12 at 12:40
3

You could get the length of the decimal part of your number this way:

var value = 192.123123;
stringValue = value.toString();
length = stringValue.split('.')[1].length;

It makes the number a string, splits the string in two (at the decimal point) and returns the length of the second element of the array returned by the split operation and stores it in the 'length' variable.

Florian Humblot
  • 1,121
  • 11
  • 29
  • OP stated that the numbers are already strings. Your answer does not provide anything new that hasn't already been suggested by Jack and Daniel Bidulock – Mike Scotty May 19 '17 at 08:51
  • This is by far the most straightforward answer to OP's question. OP does not ask for other notations to be taken into account (octal? hex?) – PEWColina Jun 30 '17 at 19:06
  • Look out for the scientific representation of the number before the decimals. It will kick in when the numbers length is 20 and above – Jens Alenius Mar 23 '18 at 14:45
2

Try using String.prototype.match() with RegExp /\..*/ , return .length of matched string -1

function retr_decs(args) {
  return /\./.test(args) && args.match(/\..*/)[0].length - 1 || "no decimal found"
}

console.log(
  retr_decs("23.456") // 3
  , retr_decs("9.450") // 3
  , retr_decs("123.01") // 2
  , retr_decs("123") // "no decimal found"
)
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    FYI, this does not count trailing 0s. `retr_decs("9.00") // "no decimal found"` – Gaʀʀʏ Mar 07 '17 at 19:13
  • @Gaʀʀʏ Returns `2` here https://jsfiddle.net/24rqjr83/. Can you reproduce `"no decimal found"` being returned at jsfiddle? – guest271314 Mar 07 '17 at 21:49
  • This is very strange. When I was testing it yesterday I was not having trailing zeros counted. I verified your jsfiddle and locally as well that the function is handling them. Weird. You have my upvote. – Gaʀʀʏ Mar 08 '17 at 20:28
2

I had to deal with very small numbers so I created a version that can handle numbers like 1e-7.

Number.prototype.getPrecision = function() {
  var v = this.valueOf();
  if (Math.floor(v) === v) return 0;
  var str = this.toString();
  var ep = str.split("e-");
  if (ep.length > 1) {
    var np = Number(ep[0]);
    return np.getPrecision() + Number(ep[1]);
  }
  var dp = str.split(".");
  if (dp.length > 1) {
    return dp[1].length;
  }
  return 0;
}
document.write("NaN => " + Number("NaN").getPrecision() + "<br>");
document.write("void => " + Number("").getPrecision() + "<br>");
document.write("12.1234 => " + Number("12.1234").getPrecision() + "<br>");
document.write("1212 => " + Number("1212").getPrecision() + "<br>");
document.write("0.0000001 => " + Number("0.0000001").getPrecision() + "<br>");
document.write("1.12e-23 => " + Number("1.12e-23").getPrecision() + "<br>");
document.write("1.12e8 => " + Number("1.12e8").getPrecision() + "<br>");
Orden
  • 589
  • 3
  • 13
1

A slight modification of the currently accepted answer, this adds to the Number prototype, thereby allowing all number variables to execute this method:

if (!Number.prototype.getDecimals) {
    Number.prototype.getDecimals = function() {
        var num = this,
            match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
        if (!match)
            return 0;
        return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
    }
}

It can be used like so:

// Get a number's decimals.
var number = 1.235256;
console.debug(number + " has " + number.getDecimals() + " decimal places.");

// Get a number string's decimals.
var number = "634.2384023";
console.debug(number + " has " + parseFloat(number).getDecimals() + " decimal places.");

Utilizing our existing code, the second case could also be easily added to the String prototype like so:

if (!String.prototype.getDecimals) {
    String.prototype.getDecimals = function() {
        return parseFloat(this).getDecimals();
    }
}

Use this like:

console.debug("45.2342".getDecimals());
Gabriel Nahmias
  • 920
  • 3
  • 15
  • 20
1

A bit of a hybrid of two others on here but this worked for me. Outside cases in my code weren't handled by others here. However, I had removed the scientific decimal place counter. Which I would have loved at uni!

numberOfDecimalPlaces: function (number) {
    var match = ('' + number).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
    if (!match || match[0] == 0) {
        return 0;
    }
     return match[0].length;
}
joyBlanks
  • 6,419
  • 1
  • 22
  • 47
  • If you remove the scientific decimal place counter, you can simplify your regex: /(?:\.(\d+))?$/ Plus you use match[0] instead of match[1]. – Fl4v Feb 15 '17 at 16:41
0

Based on Liam Middleton's answer, here's what I did (without scientific notation):

numberOfDecimalPlaces = (number) => {
            let match = (number + "").match(/(?:\.(\d+))?$/);
            if (!match || !match[1]) {
                return 0;
            }

            return match[1].length;
        };
        
alert(numberOfDecimalPlaces(42.21));
Fl4v
  • 1,024
  • 11
  • 19
0
function decimalPlaces(n) {
  if (n === NaN || n === Infinity)
    return 0;
  n = ('' + n).split('.');
  if (n.length == 1) {
    if (Boolean(n[0].match(/e/g)))
      return ~~(n[0].split('e-'))[1];
    return 0;

  }
  n = n[1].split('e-');
  return n[0].length + ~~n[1];

}
dke
  • 1
0

if it's guaranteed to have digits below the radix, already in string format, no trailing non-digit bytes, and no chance of scientific notations, then why not just

  # 1-based indices

  length( decstr ) - index( decstr , "." )
RARE Kpop Manifesto
  • 2,453
  • 3
  • 11