35

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.

2
  • 4
    NB: Web Assembly has i32.popcnt, if people really, really care. Commented Aug 31, 2017 at 12:25
  • I checked.. and the fastest way is even the easier one! Check my answer and mark it as answer! stackoverflow.com/a/57631591/236062 and my code has no 32 bit limitation! Commented Aug 24, 2019 at 8:50

12 Answers 12

44

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 == 0 ? 0 : n.toString(2).match(/1/g).length
}

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

Sign up to request clarification or add additional context in comments.

12 Comments

"use a regular expression" - now you have two problems :D
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 is by far the faster option.
You can use the bithacks for larger integers too, just extract the high bits and count them separately.
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: jsperf.com/fast-int32-bit-count
@vitaly-t Note that Doin's is also slow as it causes two coercions to integer instead of one. A better approach would be to put n|=0 all throughout it and use imul to avoid weird rounding. Also I highly reccomend, if you're using Uint32Array for performance, note that Int32Array is signifigantly faster as it helps the V8 avoid costly conversions between integers and floats. (As it can't store unsigned integers without converting them to floats) E.g. keep everything as signed integers and only do unsigned arithmetic in temporary expressions where the result wont be assigned: (x>>>0)<(y>>>0)
|
7

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 bitCountBigInt (n) {
  n = BigInt(n)
  let bits = 0
  while (n !== 0n) {
    bits += Number(n & 1n)
    n >>= 1n
  }
  return bits
}
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 bitCountBigInt (n) {
      n = BigInt(n)
      let bits = 0
      while (n !== 0n) {
        bits += Number(n & 1n)
        n >>= 1n
      }
      return bits
    }
    
  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],
      [bitCountBigInt, 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.

4 Comments

@bradw2k nope: the simplest is the second fastest.. the fastest is 10 times faster than the simplest (count1s). check again.
count1s32 counts only 31 bits, not all 32 bits.
@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.
I tried the algorithms here and one found elsewhere, and it looks like calling the 32-bit version twice is faster than custom crafting a 64-bit version: jsbenchit.org/?src=744a9d122ea9423c959a164c2f6af9e9
4

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

1 Comment

Beware, though, that this will fail for integers that don't fit in a 32-bit two's complement type.
2

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

Comments

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

5 Comments

what is I33t? so looping is actually faster than creating/sorting/joining? What is a resource that I can read on this topic?
@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 jsperf.com is handy for perf comparisons (when it's up).
@MingHuang: Yeah, much faster.
I have tested my answer. it is like 800 times faster than the fastest answer you have above. jsperf.com/counting-1s-in-binary-strings/4
@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. jsperf.com/counting-1s-in-binary-string-again It is, regardless, clever.
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

3 Comments

Beware, though, that this will fail for integers that don't fit in a 32-bit two's complement type.
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, and if I'm reading that right, the 1 should be safe. Neat.
your code does not work with i=Number.MAX_SAFE_INTEGER
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.

Comments

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.

Comments

1
  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

Comments

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

2 Comments

Be careful; the string '0' is truthy as well.
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)
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);

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.