Code of the Day: Javascript Significant Figures

Did you know that JavaScript automatically trims off trailing zeros on numbers with decimals?
Well now you do, and in order to save precision you must wrap the number inside a string.
Example:

1
2
3
4
5
6
var a = "0.0100";
var b = 0.0100;                           // To save space the interpreter cuts off the last two zeros.
console.log( a === b.toString() );	// displays false because a = "0.0100" and b = 0.01
 
console.log( 1000 === 1000.000 );	// displays true
console.log( 0.1 === 0.1000 );		// displays true

Some might argue that you can use num.toPrecision() to save the trailing zeros. But what most are unaware of is that toPrecision() returns a string.

1
2
3
4
5
var num = (120.0).toPrecision(8);
console.log( num );                      // displays 120.00000
console.log( typeof num );            // displays string
console.log( +num );                    // displays 120
// +num is a shortcut for parseInt( num, 10);

Now let’s see what happens with leading zero’s.

1
2
3
4
5
6
7
//A leading 0 in front of a number converts it base 8.
// a.k.a. parseInt( num, 8);
console.log(0011);		// Displays 9
console.log( 010.1000 );		// Error! Octals can only be whole numbers.
console.log( 00000.1000 );	// Error for the same reason.
// The problem is that a leading zero will cause the number to be converted to octal.
// Thus, an error is generated because the decimal point is neither an operation nor semicolon.

Using numbers as strings can be useful when trying to find the Significant Digits, like in the following function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Returns the significant digits of a number.
// @param {Number|String} num
// @return Returns -1 if invalid input, otherwise will return a positive number.
var getSigFigs = function (num) {
  if (!isFinite(Number(num))) {
    return -1;
  }
  var n = String(num).trim(),
  FIND_FRONT_ZEROS_SIGN_DOT_EXP = /^[\D0]+|\.|([e][^e]+)$/g,
  FIND_RIGHT_ZEROS = /0+$/g;
 
  if (!/\./.test(num)) {
    n = n.replace(FIND_RIGHT_ZEROS, "");
  }
  return n.replace(FIND_FRONT_ZEROS_SIGN_DOT_EXP, "").length;
};

Usage:

1
console.log( getSigFigFromNum( "0.01230" ) == 5);

Test cases

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 
// Try using Qunit to run tests.
var assert = {};
assert.equal = function (a, b) {
  if (a != b) {
    console.error("FAIL: --> %s != %s", a, b);
  } else {
    console.log("Pass: %s == %s", a, b);
  }
};
var test = function (name, fn) {
  console.log("\n#Testing: %s", name);
  fn();
};
test("Invalid numbers", function () {
  assert.equal(getSigFigs("742400g"), -1);
  assert.equal(getSigFigs("g742400"), -1);
  assert.equal(getSigFigs("Infinity"), -1);
  assert.equal(getSigFigs("-Infinity"), -1);
  assert.equal(getSigFigs("NaN"), -1);
});
var testWithPrefixes = function (prefixes, nums, fn) {
  prefixes.forEach(function (prefix) {
    nums.forEach(function (num) {
      //assert.equal( "Testing " + expected + "" prefix+expected, fn(prefix+num));
    });
  });
};
test("Whole Numbers", function () {
  assert.equal(getSigFigs("742400"), 4);
  assert.equal(getSigFigs("742000"), 3);
  assert.equal(getSigFigs("740000"), 2);
  assert.equal(getSigFigs("700000"), 1);
});
test("Negative Whole Numbers", function () {
  assert.equal(getSigFigs("-742400"), 4);
  assert.equal(getSigFigs("-742000"), 3);
  assert.equal(getSigFigs("-740000"), 2);
  assert.equal(getSigFigs("-700000"), 1);
});
test("+Whole numbers", function () {
  assert.equal(getSigFigs("+742400"), 4);
  assert.equal(getSigFigs("+742000"), 3);
  assert.equal(getSigFigs("+740000"), 2);
  assert.equal(getSigFigs("+700000"), 1);
});
test("Decimals", function () {
  assert.equal(getSigFigs("0.07284"), 4);
  assert.equal(getSigFigs("0.0728"), 3);
  assert.equal(getSigFigs("0.072"), 2);
  assert.equal(getSigFigs("0.07"), 1);
  assert.equal(getSigFigs("123.07"), 5);
});
test("Negative Decimals", function () {
  assert.equal(getSigFigs("-0.07284"), 4);
  assert.equal(getSigFigs("-0.0728"), 3);
  assert.equal(getSigFigs("-0.072"), 2);
  assert.equal(getSigFigs("-0.07"), 1);
  assert.equal(getSigFigs("-10.07"), 4);
});
test("Decimals", function () {
  assert.equal(getSigFigs("+0.07284"), 4);
  assert.equal(getSigFigs("+0.0728"), 3);
  assert.equal(getSigFigs("+0.072"), 2);
  assert.equal(getSigFigs("+0.07"), 1);
});
test("Zero", function () {
  assert.equal(getSigFigs("0.0"), 0);
  assert.equal(getSigFigs("-0.0"), 0);
  assert.equal(getSigFigs("0"), 0);
  assert.equal(getSigFigs("+0"), 0);
  assert.equal(getSigFigs("-0"), 0);
});
 
test("E notated", function () {
  assert.equal(getSigFigs("1.3e32"), 2);
  assert.equal(getSigFigs("-1.23e-32"), 3);
  assert.equal(getSigFigs("-1.023e+120"), 4);
  assert.equal(getSigFigs("+5.023e+32"), 4);
  assert.equal(getSigFigs("+5.0230e+32"), 5);
});

Can you think of any other useful examples for number wrapped in strings?

Larry Battle

I love to program, and discover new tech. Check out my stackoverflow and github accounts.

More Posts - Website

Follow Me:
Twitter

  • PN

    Unknown Unknown

    does this function accept variables (i.e. “g2” ) as input. It does not seem to work if the input is a variable that references a number and not a number itself.

    • Larry Battle

      Unknown Unknown

      Sorry for the late response.
      The issue can be fixed by enforcing the input to be string.
      I’ll update the code to do this.

  • PN

    Unknown Unknown

    actually never mind, I just got it to work…

    if your input is a variable and not a number itself, do this first

    var numberitself = 15;
    var numberstring = num.toString();

    now run the function with numberstring as input

    alert(getSigFigFromNum( numberstring ));

  • PN

    Unknown Unknown

    this does not work if there is both a decimal and trailing zeros on the right, i.e. 0.001540
    It will only count three sigfigs, but that number in the example has four.

    • Larry Battle

      Unknown Unknown

      This is now fixed. Please check out the latest code.

  • PN

    Unknown Unknown

    I finally got it to work, but not without tons of extra (probably inefficient) code added. I can explain, but not easily.. if anyone wants to see what I did, let me know

    • Larry Battle

      Unknown Unknown

      Thanks alot! I rewrote the code and included test cases. Please let me that if there are any more bugs. Thanks again!