Reputation: 79440
I have this so far (which is all bitwise, not using Math
functions):
function nextPowerOf2(n) {
if (n && !(n & (n - 1))) {
return n
}
let p = 1
while (p < n) {
p <<= 1
}
return p
}
To get the exponent though, I have to do this:
const exp = Math.log2(nextPowerOf2(val))
Is there any way to do this without Math.log
, using more primitive operations? Which way is better performing?
Upvotes: 0
Views: 95
Reputation: 2713
We can count number of bits in a number - that will be log2(nextPowerOf2):
const log2ofNextPowerOf2 = (x) => {
if (!x) return 0;
let c = 0, b = 0;
while (x > 0) {
c++; b += x & 1;
x = x >> 1;
}
return b == 1 ? c - 1 : c;
}
// test it:
[0, 1, 2, 3, 4, 5, 8, 100].forEach(n => console.log(n, '=>', log2ofNextPowerOf2(n)));
Compare this to your original function:
function nextPowerOf2(n) {
if (n && !(n & (n - 1))) {
return n
}
let p = 1
while (p < n) {
p <<= 1
}
return p
}
// test it:
[0, 1, 2, 3, 4, 5, 8, 100].forEach(n => console.log(n, '=>', Math.log2(nextPowerOf2(n))));
Upvotes: 2
Reputation: 68
In my experience Math.log2 is pretty performant, especially when compared to other options using bitwise and even lookup tables.
If you would like to see how it could be done with bitwise here is a great resource: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
Here is an example adapted for JS from C++ (be careful of numbers past Number.MAX_SAFE_INTEGER
) which is ~59% slower
function log2(v) {
let b = [0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000];
let S = [1, 2, 4, 8, 16, 32];
let i;
let r = 0;
for (i = 5; i >= 0; i--){
if (v & b[i]) {
v >>= S[i];
r |= S[i];
}
}
return r;
}
log2(nextPowerOf2(348209348));
Upvotes: 1