53

Is there a better way of figuring out the number of decimals on a number than in my example?

var nbr = 37.435.45;
var decimals = (nbr!=Math.floor(nbr))?(nbr.toString()).split('.')[1].length:0;

By better I mean faster to execute and/or using a native JavaScript function, ie. something like nbr.getDecimals().

Thanks in advance!

EDIT:

After modifying series0ne answer, the fastest way I could manage is:

var val = 37.435345;
var countDecimals = function(value) {
    if (Math.floor(value) !== value)
        return value.toString().split(".")[1].length || 0;
    return 0;
}
countDecimals(val);

Speed test: http://jsperf.com/checkdecimals

PhilTrep
  • 1,172
  • 1
  • 8
  • 23
  • 2
    Have a look at this very thorough answer http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number – Reinstate Monica Cellio Jun 28 '13 at 16:12
  • Thank you but the asker specified that there has to be a decimal in the numbers whereas I do not. +1 for the interesting link – PhilTrep Jun 28 '13 at 16:18
  • @PhilippeTrépanier, consider what it more important in the grand scheme of things. How fast the JavaScript executes (the different between my answer and Pete D's is minimal), and the speed it downloads to the client? Since Pete D's answer is a few bytes shorter, Personally I'd be in a predicament on which one to choose. – Matthew Layton Jun 28 '13 at 16:45
  • @series0ne, I am in a predicament but since you gave an explanation on the working of your code, you gave the prototypal way of doing it and your answer was faster than his, I gave the correct answer to you. – PhilTrep Jun 28 '13 at 16:50
  • @PhilippeTrépanier, Fair enough, I can't argue with that! :-) thanks. – Matthew Layton Jun 28 '13 at 16:51

3 Answers3

108
Number.prototype.countDecimals = function () {
    if(Math.floor(this.valueOf()) === this.valueOf()) return 0;
    return this.toString().split(".")[1].length || 0; 
}

When bound to the prototype, this allows you to get the decimal count (countDecimals();) directly from a number variable.

E.G.

var x = 23.453453453;
x.countDecimals(); // 9

It works by converting the number to a string, splitting at the . and returning the last part of the array, or 0 if the last part of the array is undefined (which will occur if there was no decimal point).

If you do not want to bind this to the prototype, you can just use this:

var countDecimals = function (value) {
    if(Math.floor(value) === value) return 0;
    return value.toString().split(".")[1].length || 0; 
}
Matthew Layton
  • 32,574
  • 37
  • 140
  • 255
  • Thank you, that is exactly what I need! I'll accept as soon as my cooldown is gone... – PhilTrep Jun 28 '13 at 16:21
  • No problem, you're welcome! :-) – Matthew Layton Jun 28 '13 at 16:22
  • 8
    This will produce an error for integer number: http://jsfiddle.net/3bQqV/ But maybe it is not a problem for OP – A. Wolff Jun 28 '13 at 16:24
  • @roasted, well spotted, I'll work on that – Matthew Layton Jun 28 '13 at 16:26
  • using modulo as Pete D's answer would be faster, anyway +1 – A. Wolff Jun 28 '13 at 16:31
  • following my last comment, no, it is faster to use floor() method – A. Wolff Jun 28 '13 at 16:46
  • @Math.floor is probably faster because it's a [native code] function, meaning it's already compiled into the JS runtime, whereas the modulo equivalent needs to be interpreted/JIT'ed. – Matthew Layton Jun 28 '13 at 16:50
  • What if I need to know the decimals of a number entered inside an input? I'm trying this: `console.log($('#myinput').val().countDecimals());` but the result is: **Uncaught TypeError: undefined is not a function** – Andres SK Oct 19 '14 at 04:37
  • for number 23.00 it shows count as 0 – Amaldev ps Oct 31 '18 at 07:12
  • 2
    Also produces error with very small decimal amounts. (e.g 0.000000001) http://jsfiddle.net/yh2Lurf5/ – P Roitto Jan 22 '19 at 07:53
  • 4
    Some countries use comma as decimal separator, not a period. I've used this quick'n'dirty detection trick in the past to deal with that: `var isComma = Boolean(parseFloat(1 / 2).toString().indexOf(",") !== -1);` your last line would become: `return value.toString().split((isComma ? "," : "."))[1].length || 0;` – Balage Mar 22 '19 at 17:01
  • this wont work for when decimal value is zero. let say 10.0 it returns 0 since we are doing Math.floor(value) is equal to value. only return this.toString().split(".")[1].length || 0; is enough for the solution – jaibalaji Feb 05 '20 at 06:12
  • I rather use it as a function, then you get used to having this in Number and when you go to another project you don't have it. Composition is more flexible. – Gubatron May 09 '20 at 15:50
  • 2
    Now with optional chaining beeing available, this might make the function safer (eg. NaN) ```value.toString().split(".")[1]?.length || 0;``` https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Optionale_Verkettung – Andreas Riedmüller Jun 18 '20 at 11:01
  • @AndreasRiedmüller `null`, `undefined` and `NaN` will evaluate to `0` because of the `|| 0`, but good point all the same. – Matthew Layton Jun 18 '20 at 11:33
  • 1
    This is broken for some numbers, 0.0000001 for example. – user3437245 Feb 27 '21 at 14:29
  • Also this will produce error for exponential numbers like `8e-7` – wmbtrmb May 17 '21 at 12:31
33

Adding to series0ne answer if you want to have the code not throw an error for an integer number and get a result of 0 when there are no decimals use this:

var countDecimals = function (value) { 
    if ((value % 1) != 0) 
        return value.toString().split(".")[1].length;  
    return 0;
};
Matthew Layton
  • 32,574
  • 37
  • 140
  • 255
Pete D
  • 647
  • 5
  • 15
  • 2
    maybe faster like it: `var countDecimals = function (value) { return value % 1?value.toString().split(".")[1].length:0; };` – A. Wolff Jun 28 '13 at 16:30
  • @OP you should test answers in jsperf to see which seems the best one – A. Wolff Jun 28 '13 at 16:33
  • 1
    As JavaScript is a size critical language, you could omit the else statement. ie.. if(true) return x; return 0; ... basically if it does not return the amount of decimals, just return 0; – Matthew Layton Jun 28 '13 at 16:34
  • yeap it would be faster using ternary but I think it's easier to read and understand the concept. OP can change that concept to a more faster way. – Pete D Jun 28 '13 at 16:36
  • 1
    @roasted did that, series0ne's the fastest http://jsperf.com/checkdecimals – PhilTrep Jun 28 '13 at 16:38
  • @PhilippeTrépanier thank you, interresting result! – A. Wolff Jun 28 '13 at 16:45
  • Actually now that series0ne edited my code by removing the else statement its faster than his original. Good job series0ne +1 EDIT: removed false jsperf. My bad. – Pete D Jun 28 '13 at 16:46
  • @PeteD your jsperf was wrong, here is the correct one: http://jsperf.com/checkdecimals/4 – A. Wolff Jun 28 '13 at 16:48
2

Regex are very very not efficient in calculations, they are mean if you can go another way. So I would avoid them :)

Things like "Number % 1" return the rounded decimal value (2.3 % 1 = 0.2999999999999998), and in a general way I would advice you to use strings as early as possible, since approximations with real numbers can change the number of decimals.

So yours is fine, but I'll look a way to optimize it.

Edit:

function CountDecimalDigits(number)
{
  var char_array = number.toString().split(""); // split every single char
  var not_decimal = char_array.lastIndexOf(".");
  return (not_decimal<0)?0:char_array.length - not_decimal;
}
Ricola3D
  • 2,110
  • 14
  • 15
  • 1
    You have an off by one error. `char_array.length - not_decimal` should be `char_array.length - not_decimal - 1` – aloisdg Mar 08 '20 at 14:08