31

Let's say I have an integer I and want to get the count of 1s in its binary form.

I am currently using the following code.

Number(i.toString(2).split("").sort().join("")).toString().length;

Is there a faster way to do this? I am thinking about using bitwise operators. Any thoughts?

NOTE: i is within the 32-bit limitation.

gyre
  • 16,369
  • 3
  • 37
  • 47
Ming Huang
  • 1,310
  • 3
  • 16
  • 25
  • 4
    NB: Web Assembly has `i32.popcnt`, if people *really, really care*. – Veedrac Aug 31 '17 at 12:25
  • I checked.. and the fastest way is even the easier one! Check my answer and mark it as answer! https://stackoverflow.com/a/57631591/236062 and my code has no 32 bit limitation! – Zibri Aug 24 '19 at 08:50

12 Answers12

40

You can use a strategy from this collection of Bit Twiddling Hacks:

function bitCount (n) {
  n = n - ((n >> 1) & 0x55555555)
  n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
  return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
}

console.log(bitCount(0xFF)) //=> 8

Note that the above strategy only works for 32-bit integers (a limitation of bitwise operators in JavaScript).

A more general approach for larger integers would involve counting 32-bit chunks individually (thanks to harold for the inspiration):

function bitCount (n) {
  var bits = 0
  while (n !== 0) {
    bits += bitCount32(n | 0)
    n /= 0x100000000
  }
  return bits
}

function bitCount32 (n) {
  n = n - ((n >> 1) & 0x55555555)
  n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
  return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
}

console.log(bitCount(Math.pow(2, 53) - 1)) //=> 53

You could also use a regular expression:

function bitCount (n) {
  return n.toString(2).match(/1/g).length
}

console.log(bitCount(0xFF)) //=> 8
Community
  • 1
  • 1
gyre
  • 16,369
  • 3
  • 37
  • 47
  • You caught me just as I was posting that footnote :) – gyre Mar 30 '17 at 15:33
  • I am pretty new in this bitwise world. What is 0x55555555 and 0xF0F0F0F? What are some good resource that I can read up on this? thanks. – Ming Huang Mar 30 '17 at 15:39
  • That is hexadecimal notation for number literals. The `0x` just tells you that it's hexadecimal; the rest of the literal is what you would get by calling `number.toString(16)`. See [this article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Integers) on MDN. – gyre Mar 30 '17 at 15:41
  • 1
    _"use a regular expression"_ - now you have two problems :D – Joseph Mar 30 '17 at 15:42
  • I was just [making fun of regex solutions](https://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/). Regex is perfectly fine on this one tho. :D – Joseph Mar 30 '17 at 15:46
  • 2
    I like the regex solution, but if it's "faster" the OP's looking for, regex isn't going to be it. [The boring option](http://stackoverflow.com/a/43122287/157247) is [by far the faster option](https://jsperf.com/counting-1s-in-binary-strings). – T.J. Crowder Mar 30 '17 at 15:55
  • @TJCrowder I'm not too surprised . . . I figured it wouldn't be fair to post a for-loop solution given that you had already done so. That's why I went for "terse" instead (and the regex should still be faster than OP's original implementation). – gyre Mar 30 '17 at 16:43
  • 1
    You can use the bithacks for larger integers too, just extract the high bits and count them separately. – harold Mar 30 '17 at 17:08
  • Good point @harold . . . let me see if I can cook something up along those lines. – gyre Mar 30 '17 at 17:10
  • 1
    In Javascript, the bitCount() function from bit twiddling hacks can be further sped up by (a) doing an initial convert-to-int32: `n = (n|0) - ((n >> 1) & 0x55555555)`, and (b) slightly by using >>> instead of >> everywhere. See: https://jsperf.com/fast-int32-bit-count – Doin Nov 09 '19 at 15:24
  • This counts only 31 bits, not all 32 bits. I'm trying to count bits inside `Uint32Array`, and the code doesn't work. – vitaly-t Oct 20 '21 at 20:07
  • [Count the number of set bits in a 32-bit integer](https://stackoverflow.com/a/109025) recommends `(n|0)` for the first step, as Doin points out, to coerce to integer. – Peter Cordes Nov 13 '22 at 07:15
5

A recursive very nice but slow way:

    function count1(n, accumulator=0) {
      if (n === 0) {
        return accumulator
      }
      return count1(n/2, accumulator+(n&1))
    }

console.log(count1(Number.MAX_SAFE_INTEGER));

But if you want a very fast one (faster than T.J. Crowder answer)):

    count1s=(n)=>n.toString(2).replace(/0/g,"").length

console.log(count1s(Number.MAX_SAFE_INTEGER));

Note: some of the other solutions do not work with bit integers (> 32 bit) these two do!

Now, if we consider only 32 bit numbers, the fastest way is this:

function count1s32(i) {
  var count = 0;
  i = i - ((i >> 1) & 0x55555555);
  i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
  i = (i + (i >> 4)) & 0x0f0f0f0f;
  i = i + (i >> 8);
  i = i + (i >> 16);
  count += i & 0x3f;
  return count;
}

  console.log(count1s32(0xffffffff));

https://jsperf.com/count-1/1

53 bit comparison:

53 bit test

32 bit comparison:

32 bit test

Benchmark here! (since jsperf is often down).

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {

  time_function = function(ms, f, num) {
    var z;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++) f(num);
    return (z / ms)
  } // returns how many times the function was run in "ms" milliseconds.

  // two sequential loops
  count1s = (n) => n.toString(2).replace(/0/g, "").length

  // three loops and a function.
  count1j = (n) => n.toString(2).split('').filter(v => +v).length

  /*  Excluded from test because it's too slow :D
  
      function count1(n, accumulator=0) {
          if (n === 0) {
              return accumulator
          }
          return count1(n / 2, accumulator + (n & 1))
      }
  */

  function countOnes(i) {
    var str = i.toString(2);
    var n;
    var count = 0;
    for (n = 0; n < str.length; ++n) {
      if (str[n] === "1") {
        ++count;
      }
    }
    return count;
  } // two sequential loops ( one is the toString(2) )

  function count1sb(num) {
    i = Math.floor(num / 0x100000000);
    //      if (i > 0) {
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = (i + (i >> 4)) & 0x0f0f0f0f;
    i = i + (i >> 8);
    i = i + (i >> 16);
    count = i & 0x3f;
    i = num & 0xffffffff;
    //      }
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = (i + (i >> 4)) & 0x0f0f0f0f;
    i = i + (i >> 8);
    i = i + (i >> 16);
    count += i & 0x3f;
    return count;
  }

  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }



    funcs = [
      [count1s, 0],
      [count1j, 0],
      [count1sb, 0],
      [countOnes, 0]
    ];

    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff[0].name);
      ff[1] = time_function(2500, ff[0], Number.MAX_SAFE_INTEGER);
      console.log("Score: " + ff[1]);

    })
    return funcs.sort(compare);
  }

  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();
console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea cols=80 rows=30 id="log"></textarea>

The benchmark will run for 10s.

Zibri
  • 9,096
  • 3
  • 52
  • 44
  • @bradw2k nope: the simplest is the second fastest.. the fastest is 10 times faster than the simplest (count1s). check again. – Zibri Nov 04 '19 at 17:31
  • `count1s32` counts only 31 bits, not all 32 bits. – vitaly-t Oct 20 '21 at 20:08
  • @vitaly-t it certainly does count to 32. However, you'll likely pay for a `not a SMI` deopt if you use more than 30 bits. JS engines use LSB to encode whether an integer is a pointer or not. – Ryder Brooks Jan 18 '22 at 23:22
3

Doing n = n & (n - 1) you removing last 1 bit in the number. According to this, you can use the following algorithm:

function getBitCount(n) {
  var tmp = n;
  var count = 0;
  while (tmp > 0) {
    tmp = tmp & (tmp - 1);
    count++;
  }
  return count;
}

console.log(getBitCount(Math.pow(2, 10) -1));
eldarg
  • 308
  • 2
  • 7
1

Given that you're creating, sorting, and joining an array, if it's literally faster you want, you're probably better off doing it the boring way:

console.log(countOnes(8823475632));

function countOnes(i) {
  var str = i.toString(2);
  var n;
  var count = 0;
  for (n = 0; n < str.length; ++n) {
    if (str[n] === "1") {
      ++count;
    }
  }
  return count;
}

(Use str.charAt(n) instead of str[n] if you need to support obsolete browsers.)

It's not as l33t or concise, but I bet it's faster it's much faster:

enter image description here

...and similarly on Firefox, IE11 (IE11 to a lesser degree).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • what is I33t? so looping is actually faster than creating/sorting/joining? What is a resource that I can read on this topic? – Ming Huang Mar 30 '17 at 15:38
  • @MingHuang: Given that there are loops involved in creating, sorting, and joining the arrays, yeah, I bet it's faster. I also bet 99.9999% of the time, it doesn't matter. I don't have any specific resources for you other than that http://jsperf.com is handy for perf comparisons (when it's up). – T.J. Crowder Mar 30 '17 at 15:46
  • @MingHuang: Yeah, [much faster](https://jsperf.com/counting-1s-in-binary-strings). – T.J. Crowder Mar 30 '17 at 15:54
  • I have tested my answer. it is like 800 times faster than the fastest answer you have above. https://jsperf.com/counting-1s-in-binary-strings/4 – Keyang Mar 31 '17 at 12:41
  • @Keyang: Neat. A couple of things: 1. That test is flawed, because you modify `num`, which is reused between tests. 2. You didn't include the check for a correct result, which would have shown you you were comparing apples to oranges. :-) If we compare apples-to-apples, Chrome makes it about 4x faster, while Firefox says it's a third slower. https://jsperf.com/counting-1s-in-binary-string-again It is, regardless, clever. – T.J. Crowder Mar 31 '17 at 15:07
1

Below works fine with any number:

var i=8823475632,count=0;while (i=Math.floor(i)) i&1?count++:0,i/=2
console.log(count); //17

change the i to the value you want or wrap it as a function

if integer is within 32-bit , below works

var i=10,count=0;while (i) i&1?count++:0,i>>=1
Keyang
  • 1,828
  • 1
  • 12
  • 17
  • Beware, though, that this will fail for integers that don't fit in a 32-bit two's complement type. – T.J. Crowder Mar 30 '17 at 15:47
  • Had to check the spec about whether the low-order 1 would be preserved correctly in that `num&1` above. I'm fairly sure it is. `&` coerces via [ToInt32](http://www.ecma-international.org/ecma-262/7.0/index.html#sec-toint32), and if I'm reading that right, the 1 should be safe. Neat. – T.J. Crowder Mar 31 '17 at 15:11
  • your code does not work with i=Number.MAX_SAFE_INTEGER – Zibri Aug 23 '19 at 20:24
1

if you want to use an absolute one liner solution you can have a look at this.

countBits = n => n.toString(2).split('0').join('').length;

1.Here n.toString(2) converts n into binary string

2.split('0') makes array out of the binary string splitting only at 0's and hence returning an array of only 1 present in binary of n

3.join('') joins all one and making a string of 1s

4.length finds length of the string actually counting number of 1's in n.

eon grey
  • 101
  • 2
  • 2
1

A few more "fun" 1-liners:

Recursive: count each bit recursively until there's no more bits set

let f = x => !x ? 0 : (x & 1) + f(x >>= 1);

Functional: split the base 2 string of x and return the accumulated length of bits set

g = x => x.toString(2).split('0').map(bits => bits.length).reduce((a, b) => a + b);
claytonjwong
  • 814
  • 1
  • 7
  • 13
1

Keeps on checking if last bit is 1, and then removing it. If it finds last bit is one, it adds it to its result.

Math.popcount = function (n) {
  let result = 0;

  while (n) {
    result += n % 2;
    n = n >>> 1;
  };

  return result;
};

console.log(Math.popcount(0b1010));

For 64 bits, you can represent the number as two integers, the first is the top 32 digits, and the second is the bottom 32. To count number of ones in 64 bits, you can seperate them into 2, 32 bit integers, and add the popcount of the first and second.

Crupeng
  • 317
  • 2
  • 14
0

You can skip Number, sort and the second toString. Use filter to only consider the 1s (a truthy value) in the array then retrieve how many got through with length.

i.toString(2).split('').filter(v => +v).length
Joseph
  • 117,725
  • 30
  • 181
  • 234
  • 2
    Be careful; the string `'0'` is truthy as well. – gyre Mar 30 '17 at 15:45
  • `reduce` better-communicates meaning here, in my opinion (take a sequence of values and return a single value). It also has the advantage of not having to built an array of `'1'`s in this case: `i.toString(2).split('').reduce((sum, char) => char === '1' ? ++sum : sum, 0)` – Max Heiber Jul 03 '17 at 03:11
0

Simple solution if you just want to count number of bit!

const integer = Number.MAX_SAFE_INTEGER;
integer.toString(2).split("").reduce((acc,val)=>parseInt(acc)+parseInt(val),0);
TrickOrTreat
  • 821
  • 1
  • 9
  • 23
0
  1. Regex
const bitCount = (n) => (n.toString(2).match(/1/g) || []).length;
  1. Bitwise AND, Right Shift
    function bitCount(n) {
        let count = 0;
        while(n) {
            count += n & 1;
            n  >>= 1; 
        }
        return count;
    }
  1. Brian Kernighan algorithm
    function bitCount(n) {
        let count = 0;
        while(n) {
            n &= (n-1); 
            count ++;
        }
        return count;
    }

test:

    bitCount(0) // 0
    bitCount(1) // 1
    bitCount(2) // 1
    bitCount(3) // 2
aermin
  • 341
  • 3
  • 8
0

Or, take any of the above functions, generate an array with values for (0..255), (save the array) and do a lookup for each 8 bit group. For an n-bit number takes ceil((n-1)/8) shifts, ands and table lookups.

Cuse70
  • 271
  • 3
  • 5