Is there a reliable way in JavaScript to obtain the number of decimal places of an arbitrary number?

前端 未结 7 1343
一整个雨季
一整个雨季 2020-12-01 10:15

It\'s important to note that I\'m not looking for a rounding function. I am looking for a function that returns the number of decimal places in an arbitrary number\'s simpli

7条回答
  •  情深已故
    2020-12-01 11:07

    2017 Update

    Here's a simplified version based on Edwin's answer. It has a test suite and returns the correct number of decimals for corner cases including NaN, Infinity, exponent notations, and numbers with problematic representations of their successive fractions, such as 0.0029 or 0.0408. This covers the vast majority of financial applications, where 0.0408 having 4 decimals (not 6) is more important than 3.14e-21 having 23.

    function decimalPlaces(n) {
      function hasFraction(n) {
        return Math.abs(Math.round(n) - n) > 1e-10;
      }
    
      let count = 0;
      // multiply by increasing powers of 10 until the fractional part is ~ 0
      while (hasFraction(n * (10 ** count)) && isFinite(10 ** count))
        count++;
      return count;
    }
    
    for (const x of [
      0.0028, 0.0029, 0.0408, 0.1584, 4.3573, // corner cases against Edwin's answer
      11.6894,
      0, 1.0, 1.00, 0.123, 1e-3, -1e2, -1e-2, -0.1,
      NaN, 1E500, Infinity, Math.PI, 1/3,
      3.14, 2.e-3, 2.e-14,
      1e-9,  // 9
      1e-10,  // should be 10, but is below the precision limit
      -3.14e-13,  // 15
      3.e-13,  // 13
      3.e-14,  // should be 14, but is below the precision limit
      123.12345678901234567890,  // 14, the precision limit
      5555.0, 5555, 555.5, 555.50, 0.0000005, 5e-7, 0.00000055, 5e-8,
      0.000006, 0.0000007,
      0.123, 0.121, 0.1215
    ]) console.log(x, '->', decimalPlaces(x));

    The tradeoff is that the method is limited to maximum 10 guaranteed decimals. It may return more decimals correctly, but don't rely on that. Numbers smaller than 1e-10 may be considered zero, and the function will return 0. That particular value was chosen to solve correctly the 11.6894 corner case, for which the simple method of multiplying by powers of 10 fails (it returns 5 instead of 4).

    However, this is the 5th corner case I've discovered, after 0.0029, 0.0408, 0.1584 and 4.3573. After each, I had to reduce the precision by one decimal. I don't know if there are other numbers with less than 10 decimals for which this function may return an incorrect number of decimals. To be on the safe side, look for an arbitrary precision library.

    Note that converting to string and splitting by . is only a solution for up to 7 decimals. String(0.0000007) === "7e-7". Or maybe even less? Floating point representation isn't intuitive.

提交回复
热议问题