计算数字有效位数的最快方法是什么?

时间:2014-04-05 18:07:37

标签: javascript

计算数字有效位数的最快方法是什么?

我有以下功能,但是由于字符串操作,它很慢。

/**
 * 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
};

有更快的方法吗?

更新:此处列出了测试正确功能的断言:

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);

digits(0)我自己的方法失败了,我通过向第二个正则表达式添加?来解决这个问题。

7 个答案:

答案 0 :(得分:6)

这是一种更加数学化的方式来做同样的操作(看起来速度要快得多)

JSPerf比较三种实现

http://ecma262-5.com/ELS5_HTML.htm#Section_8.5的整数n < +-(2^53) 准确 将浮点数转换为字符串,然后强制转换为int(通过删除小数,因此应用类似的规则)

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
}

答案 1 :(得分:2)

正则表达式的轻微改进

function digits (value) {
  return value
      .toExponential()
      .replace(/^([0-9]+)\.?([0-9]+)?e[\+\-0-9]*$/g, "$1$2")
      .length
};

答案 2 :(得分:2)

还有另一种方法,它使用字符串操作并处理一些特殊情况以获得更好的性能:

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;
}

答案 3 :(得分:1)

定期检查字符串。虽然略有改善。

function digits(value) {
  value = "" + value;
  var res = 0;
  for (var i = 0, len = value.length; i < len; i++){
    if (value[i]==="e")break;
    if (+value[i]>=0)
      res++;
}
  return res;
};

与OP和其他答案代码相比,

jsperf Benchmark testing result

答案 4 :(得分:1)

您可以使用类型化数组直接检查浮点值的字节。这样做的好处在于速度快,并且不需要进行任何数学计算。您可以直接查看尾数的

你可以从这开始:

var n = yourFloatingPointValue; 

var f64 = new Float64Array(1);
var dv = new DataView(f64.buffer);

dv.setFloat64(0, n, false); // false -> big-endian

var bytes = [];
for (var i = 0; i < 8; i++)
  bytes.push(dv.getUint8(i));

现在bytes数组包含表示内存中浮点值的8位值的整数。第一个字节包含顶部位位置的符号位,其余部分包含指数的前7位。第二个字节包含指数的5个最低有效位和尾数的前三个位。其余的字节都是尾数。

答案 5 :(得分:0)

Fastest solutions

&#13;
&#13;
function significantDigits(n) {
    var x = Math.abs(n);
    if (x===0) return 0;
    var p = Math.floor(Math.log10(x));
    if (p>0) x = x / (10**p); 
    else if (p<0) x = x * (10**-p);
    return (x+'').length-1||1;
}
function significantDigitsAlt1(n) {
    if (n === 0) return 0;
    const s = n.toExponential(), i = s.lastIndexOf('e');
    return s[1]==='.' ? i - 1 : i;
}

[
    [0, 0],
    [2, 1],
    [1234, 4],
    [2.34, 3],
    [3000, 1],
    [0.0034, 2],
    [120.5e50, 4],
    [1120.5e+50, 5],
    [120.52e-50, 5],
    [Math.PI, 16]
].forEach(([value, expected]) => {
    console.assert(significantDigits(value) === expected);
    console.assert(significantDigitsAlt1(value) === expected);
})
console.log('all tests passed');
&#13;
&#13;
&#13;

答案 6 :(得分:-1)

有一种更快、更间接的方法,即将其转换为字符串并找到它的长度。

a = 2.303
sig_fig = len(str(a))-len(str(int(a)))-1

额外的 -1 用于“.”