简体   繁体   中英

In JavaScript, can bit shifting be used to isolate 1 or more bits in a byte?

In JavaScript code where the 8 bits of a byte represent 8 Boolean "decisions" (aka: flags), there is a need to isolate each given bit for conversion to a Boolean variable. Consider my solution using String parsing:

var bitParser = function (_nTestByte, _nBitOrdinal) {
  var bits = ("00000000" + _nTestByte.toString(2)).slice(-8); // convert to binary and zero-pad
  return bits[_nBitOrdinal] === "1";
};

console.log(bitParser(0b10100101, 2)); // ECMAScript 6+ prefix, returns true

It works, and shows the desired result. However I have a hypothesis stating that a bit shifting technique would be a faster option than String manipulation. I tend to believe that but desire to prove it.

The problem is, I have yet to produce such a function that works correctly, let alone something I can test. I have created the following logic plan that I believe is accurate:

/*
  LOGIC PLAN
  ----------
  0) Remember: all bitwise operators return 32 bits even though we are using 8
  1) Left shift until the desired bit is the left-most (highest) position;
  2) Right shift (zero filling) 31 bits to eliminate all right bits
*/

The implementation of the login plan follows. Because of the 32 bit nature of bitwise operators, its my belief that the entire left 3 bytes (24 bits) must be shifted off first before we even reach the byte being worked on. Then, assuming a scenario where the 3rd bit from the left (String ordinal 2) is the desired bit, I am shifting off 2 more bits (ordinals 0 & 1), for a total of 26 bits of left shifting.

This should produce a binary number with the desired bit all the way left followed by 31 undesired zero bytes. Right shifting those 31 bits away produces a binary with 31 (now) leading zero bits which evaluates to whatever the value of the desired bit is. But of course, I would not be writing this question if THAT were true, now would I? :-)

// hardcoded, assuming the second "1" (ordinal 2) is the bit to be examined
console.log((0b10100101 << 26) >> 31); // instead of 1, returns -1

I feel like I am really close, but missing something or pushing JavaScript too hard (lol).

In JavaScript code where the 8 bits of a byte represent 8 Boolean "decisions" (aka: flags), there is a need to isolate each given bit for conversion to a Boolean variable...

If that's the actual goal, bitshifting is neither necessary nor useful: Just use a bitwise & with the desired bit, which will give you either 0 or a number with that bit set. 0 is falsy, the number with a bit set is truthy. You can either use that as-is, or force it to boolean via !!flag or Boolean(flag) :

Here's your bitParser function using bitmasking:

 var bitParser = function (_nTestByte, _nBitOrdinal) { return !!(_nTestByte & Math.pow(2, _nBitOrdinal)); }; console.log(bitParser(0b10100101, 2)); // true console.log(bitParser(0b10100101, 1)); // false 

Rather than doing the Math.pow every time, of course, we'd probably be better off with a lookup table:

 var bits = [ 0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000 ]; var bitParser = function (_nTestByte, _nBitOrdinal) { return !!(_nTestByte & bits[_nBitOrdinal]); }; console.log(bitParser(0b10100101, 2)); // true console.log(bitParser(0b10100101, 1)); // false 

From your question I took

console.log((0b10100101 << 26) >> 31); //instead of 1, returns -1 console.log((0b10100101 << 26) >> 31); //instead of 1, returns -1 .

And to answer your question why it returned -1 instead of 1

You need to do unsigned right shift >>> instead of signed one >>

 console.log((0b10100101 << 26 ) >>>31); 

Yes it can, and what you're doing is almost correct.

Integers are represented as a 32bit binary number, with the leftmost bit representing the sign (it's 1 if the number is negative and 0 if the number is positive). Lets look at some of the numbers' representations:

//last 31 digits keeps increasing as the number decreases
// ...
-2 => 0b11111111111111111111111111111110
-1 => 0b11111111111111111111111111111111
 0 => 0b00000000000000000000000000000000
 1 => 0b00000000000000000000000000000001
 2 => 0b00000000000000000000000000000010
// ...
// last 31 digits keep increasing as the number increases

Now, what you're having (0b10100101 << 26) should give you 10010100000000000000000000000000 , which you'd expect to be a big negative number (because the left-most bit is 1 ). Then right afterwards, you have >> 31 which you're expecting to strip off all 31 bits and leave you with the left-most bit.

That should work, but it's not what's happening. And why is that? It's because the people who came up with ECMAScript thought it would make more sense if 4 >> 1 returns 2 and -4 >> 1 returns -2 .

4 >> 1 // returns 2 which is 0b00000000000000000000000000000010
0b0000000000000000000000000000000100 >> 1 // returns 2, same

-4 >> 1 // returns -2, which is 0b11111111111111111111111111111110

But -4 is 0b11111111111111111111111111111100 , and for your purposes right shifting it by 1 should yield 0b01111111111111111111111111111110 (big positive number, since left-post bit is 0), and that's not -2 !

To overcome that, you can use the other right shift operator which doesn't care about about the sign: >>> . -4 >>> 1 is 2147483646 which is what we want.

So console.log((0b10100101 << 26) >>> 31); gives you 1 , which is what you want. You can also keep using >> and regarding any negative outcome to be a result of 1 being the chosen bit.

The most simple way to achieve your actual need is to use simple conditions rather than trying to isolate bits.

  var bitParser = function (_nTestByte, _nBitOrdinal) { return (_nTestByte & _nBitOrdinal); }; console.log(bitParser(6, 2) ? true : false); // true console.log(bitParser(6, 1) ? true : false); // false 

I adapted the console.log() expression in a way that may seem complicated.
It's only to really show the logical result at this step, while I didn't choose to use !! inside of the function, so returning a truly/falsy value rather than true | false .

Actually this way keeps all the most simple possible, because the expected use else where in the code is if (bitParser(...)) , which automatically casts the result to boolean.

BTW, this works whatever is the _nTestByte size (may be more than 1 byte).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM