3

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

Geek Stocks
  • 2,010
  • 3
  • 27
  • 43
  • What is the actual goal? This is a **lot** more work than necessary if you just want to do bit testing for converting bitflags to booleans. – T.J. Crowder Jan 09 '17 at 12:52
  • The actual goal is to produce a function which does the job specified in the first paragraph of the question. Testing performance of the two approaches would then follow. I apologize if that was unclear. – Geek Stocks Jan 09 '17 at 13:13

4 Answers4

2

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
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I was clearly overthinking this. Concise answer with tests. Thank you. – Geek Stocks Jan 09 '17 at 13:17
  • @GeekStocks: No worries. I actually was *literally* just now replacing that old snippet with your `bitParser` converted to use this instead, because it's not obvious (though clearly it was to you) how `_nBitOrdinal` becomes the right bit value to mask with. – T.J. Crowder Jan 09 '17 at 13:18
  • @T.J.Crowder, you do mean to use a bitwise and instead of logical? – epoch Jan 09 '17 at 13:23
  • 1
    @WillieScholtz: Thank you!! Fixed. When updating the answer with `bitParser` instead of my original example, I grabbed the OP's code and didn't notice! (Note to self: Always include the negative test, too.) – T.J. Crowder Jan 09 '17 at 13:23
1

From your question I took

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);
Nadir Laskar
  • 4,012
  • 2
  • 16
  • 33
1

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.

Aiman Al-Eryani
  • 709
  • 4
  • 19
  • I will have to study this further because I don't yet see why the 0b10100101 << 26 yields a negative number in the first place. I think your answer may actually answer the question as stated in the title, even though there was most definitely an easier way to achieve the goal. – Geek Stocks Jan 09 '17 at 14:54
  • Ahhh! I think I see it! I am CREATING a negative number with << ANYTIME the desired bit happens to be a 1, aren't I? So now I am looking up >>> to better understand HOW it ignores the sign. Apparently it MUST keep the bit but just change its use of it, yes? Instead of using it to indicate a negative, it uses it as part of the value? – Geek Stocks Jan 09 '17 at 15:03
  • I think this SO answer on another question answers my remaining question on your answer better than Mozilla's docs do : http://stackoverflow.com/a/1822369/1884386 – Geek Stocks Jan 09 '17 at 15:28
  • Yes, you are creating the negative number as you stated =). And yes, you're exactly right. `>>>` is different because it deals with the left-most bit of an integer as a part of the binary number it operates on; thus starts putting 0s from the beginning as it should. `>>` would start putting 0s starting from after the sign bit. – Aiman Al-Eryani Jan 09 '17 at 15:41
  • Excellent. Thank you. I can't help but think this question has 2 right answers. – Geek Stocks Jan 09 '17 at 15:43
0

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

cFreed
  • 4,404
  • 1
  • 23
  • 33
  • how would this get the flag if `_nBitOrdinal` is 7, i.e the 7th position; since 7 is represented as `00000111`. if `_nTestByte` is `0b01000000` the result would be 0? – epoch Jan 09 '17 at 13:11
  • @WillieScholtz The OP states " the 8 bits of a byte represent 8 Boolean "decisions" (aka: flags)". So from my point of view his need was to test `_nBitOrdinal` values like `1`, `2`, `4` and so on. In other words, each "flag" sould be a power of 2. Then my proposed solution works fine. – cFreed Jan 09 '17 at 19:35
  • @WillieScholtz BTW after examining other answers (and reading OP more carefully) I realize that the need seems to be different... andI actually don't understand what it is: why `_nTestByte` has to be expressed binary? And why its (apparent) value doesn't match what I'd find the correct result when tested? Actually I'm totally puzzled! – cFreed Jan 09 '17 at 19:48
  • @WillieScholtz And finally I just modified my answer to exactly reflect how I understand the OP. – cFreed Jan 09 '17 at 20:05