What is the fastest way to count the number of significant digits of a number?
I have the following function, which works, but is quite slow due to string operations.
/**
 * Count the number of significant digits of a number.
 *
 * For example:
 *   2.34 returns 3
 *   0.0034 returns 2
 *   120.5e+3 returns 4
 *
 * @param {Number} value
 * @return {Number} The number of significant digits
 */
function digits (value) {
  return value
      .toExponential()
      .replace(/e[\+\-0-9]*$/, '')  // remove exponential notation
      .replace( /^0\.?0*|\./, '')    // remove decimal point and leading zeros
      .length
};
Is there a faster way?
Update: here a list of assertions to test correct functioning:
assert.equal(digits(0), 0);
assert.equal(digits(2), 1);
assert.equal(digits(1234), 4);
assert.equal(digits(2.34), 3);
assert.equal(digits(3000), 1);
assert.equal(digits(0.0034), 2);
assert.equal(digits(120.5e50), 4);
assert.equal(digits(1120.5e+50), 5);
assert.equal(digits(120.52e-50), 5);
assert.equal(digits(Math.PI), 16);
My own method failed for digits(0), I fixed that by adding a ? to the second regexp.
To determine the number of significant figures in a number use the following 3 rules: Non-zero digits are always significant. Any zeros between two significant digits are significant. A final zero or trailing zeros in the decimal portion ONLY are significant.
The number of significant figures is determined by starting with the leftmost non-zero digit. The leftmost non-zero digit is sometimes called the most significant digit or the most significant figure. For example, in the number 0.004205, the '4' is the most significant figure.
Here's a more mathematical way of doing the same operation (which appears to be significantly faster)
JSPerf comparing the three implementations
Accurate for integer n < +-(2^53) per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5
Floats are converted to a string and then coerced to an int (by removing the decimal so similar rules apply)
var log10 = Math.log(10);
function getSignificantDigitCount(n) {
    n = Math.abs(String(n).replace(".", "")); //remove decimal and make positive
    if (n == 0) return 0;
    while (n != 0 && n % 10 == 0) n /= 10; //kill the 0s at the end of n
    return Math.floor(Math.log(n) / log10) + 1; //get number of digits
}
Slight improvement of regular expression
function digits (value) {
  return value
      .toExponential()
      .replace(/^([0-9]+)\.?([0-9]+)?e[\+\-0-9]*$/g, "$1$2")
      .length
};
And yet another approach, that uses string operations and handles some special cases for better performance:
function digits(value) {
    if (value === 0) {
        return 0;
    }
    //create absolute value and
    var t1 = ("" + Math.abs(value));
    //remove decimal point
    var t2 = t1.replace(".","");
    //if number is represented by scientific notation,
    //the places before "e" (minus "-" and ".") are the
    //significant digits. So here we can just return the index
    //"-234.3e+50" -> "2343e+50" -> indexOf("e") === 4
    var i = t2.indexOf("e");
    if (i > -1) {
        return i;
    } 
    //if the original number had a decimal point,
    //trailing zeros are already removed, since irrelevant
    //0.001230000.toString() -> "0.00123" -> "000123"
    if (t2.length < t1.length) {
        // -> remove only leading zeros
        return t2.replace(/^0+/,'').length;
    }
    //if number did not contain decimal point,
    //leading zeros are already removed
    //000123000.toString() -> "123000"
    // -> remove only trailing zeros
    return t2.replace(/0+$/,'').length;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With