【问题标题】:In JavaScript, can bit shifting be used to isolate 1 or more bits in a byte?在 JavaScript 中,可以使用位移来隔离一个字节中的 1 个或多个位吗?
【发布时间】:2017-01-09 12:48:03
【问题描述】:

在 JavaScript 代码中,一个字节的 8 位代表 8 个布尔“决策”(又名:标志),需要隔离每个给定位以转换为布尔变量。考虑我使用字符串解析的解决方案:

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

它有效,并显示了所需的结果。但是,我有一个假设,即位移技术将是比字符串操作更快的选择。我倾向于相信这一点,但渴望证明这一点。

问题是,我还没有生成这样一个可以正常工作的函数,更不用说我可以测试的东西了。我创建了以下我认为准确的逻辑计划:

/*
  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
*/

登录计划的实施如下。由于按位运算符的 32 位性质,我认为在我们到达正在处理的字节之前,必须首先移出整个左侧 3 个字节(24 位)。然后,假设左起第 3 位(字符串序号 2)是所需位,我将再移出 2 位(序号 0 和 1),总共左移 26 位。

这应该产生一个二进制数,其中所需的位一直在左边,后面跟着 31 个不需要的零字节。将这 31 位右移会产生一个具有 31 个(现在)前导零位的二进制文件,其计算结果为所需位的值。但是,当然,如果那是真的,我不会写这个问题,现在我会吗? :-)

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

我觉得我真的很接近,但错过了一些东西或者太用力地推动 JavaScript (lol)。

【问题讨论】:

  • 实际目标是什么?如果您只想进行位测试以将位标志转换为布尔值,那么这比必要的工作要多很多
  • 实际目标是生成一个函数来完成问题第一段中指定的工作。随后将测试这两种方法的性能。如果不清楚,我深表歉意。

标签: javascript


【解决方案1】:

在 JavaScript 代码中,一个字节的 8 位代表 8 个布尔“决策”(又名:标志),需要隔离每个给定位以转换为布尔变量...

如果这是实际目标,则位移既没有必要也没有用:只需使用带有所需位的按位&amp;,这将为您提供0 或设置了该位的数字。 0 是假的,设置位的数字是真的。您可以按原样使用它,也可以通过!!flagBoolean(flag) 强制它为布尔值:

这是您使用位掩码的bitParser 函数:

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

当然,与其每次都使用Math.pow,不如使用查找表:

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

【讨论】:

  • 我显然想多了。简洁的答案与测试。谢谢。
  • @GeekStocks:不用担心。我实际上是字面上刚刚用你的bitParser替换旧的sn-p转换为使用它,因为它并不明显(尽管对你来说很明显)_nBitOrdinal如何成为正确的位要屏蔽的值。
  • @T.J.Crowder,你的意思是按位而不是逻辑?
  • @WillieScholtz:谢谢!!固定的。当使用bitParser 而不是我的原始示例更新答案时,我抓住了 OP 的代码并没有注意到! (自我注意:也总是包括负面测试。)
【解决方案2】:

从你的问题中我接受了

console.log((0b10100101 &lt;&lt; 26) &gt;&gt; 31); //instead of 1, returns -1.

并回答您的问题,为什么它返回 -1 而不是 1

你需要做无符号右移&gt;&gt;&gt;而不是有符号的&gt;&gt;

console.log((0b10100101 &lt;&lt; 26 ) &gt;&gt;&gt;31);

【讨论】:

    【解决方案3】:

    是的,它可以,而且你所做的几乎是正确的。

    整数表示为 32 位二进制数,最左边的位表示符号(如果数字为负数则为 1,如果数字为正数则​​为 0)。让我们看一些数字的表示:

    //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
    

    现在,您所拥有的(0b10100101 &lt;&lt; 26) 应该给您10010100000000000000000000000000,您会认为它是一个很大的负数(因为最左边的位是1)。然后紧接着,你有&gt;&gt; 31,你希望去掉所有的 31 位并留下最左边的位。

    这应该可行,但事实并非如此。那为什么呢?这是因为提出 ECMAScript 的人认为如果 4 &gt;&gt; 1 返回 2-4 &gt;&gt; 1 返回 -2 会更有意义。

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

    但是 -4 是 0b11111111111111111111111111111100,为了您的目的,将它右移 1 应该得到 0b01111111111111111111111111111110(大正数,因为左后位是 0),这不是 -2

    要克服这个问题,您可以使用另一个不关心符号的右移运算符:&gt;&gt;&gt;-4 &gt;&gt;&gt; 12147483646,这是我们想要的。

    所以console.log((0b10100101 &lt;&lt; 26) &gt;&gt;&gt; 31); 给你1,这就是你想要的。您也可以继续使用&gt;&gt;,并将任何负面结果视为1 被选中的结果。

    【讨论】:

    • 我将不得不进一步研究这一点,因为我还不明白为什么 0b10100101
    • 啊!我想我看到了!我正在用 >> 以更好地了解它是如何忽略标志的。显然它必须保留它,但只是改变它的用途,是吗?它不是用它来表示否定,而是将它用作值的一部分?
    • 我认为这个 SO 对另一个问题的回答比 Mozilla 的文档更好地回答了我关于你的回答的剩余问题:stackoverflow.com/a/1822369/1884386
    • 是的,您正在创建负数,正如您所说的 =)。是的,你完全正确。 &gt;&gt;&gt; 不同,因为它将整数的最左边位作为其操作的二进制数的一部分来处理;因此,它应该从头开始放置 0。 &gt;&gt; 将从符号位之后开始放置 0。
    • 优秀。谢谢你。我不禁认为这个问题有 2 个正确答案。
    【解决方案4】:

    实现您的实际需求最简单的方法是使用简单的条件,而不是试图隔离位。

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

    我以一种看似复杂的方式调整了console.log() 表达式。
    这一步只是为了真正显示逻辑结果,而我没有选择在函数内部使用!!,所以返回一个真/假值而不是true|@987654325 @。

    实际上这种方式尽可能保持最简单,因为代码中的 else where 预期使用 if (bitParser(...)),它会自动将结果转换为布尔值。

    顺便说一句,无论_nTestByte 大小(可能超过 1 个字节),这都有效。

    【讨论】:

    • 如果_nBitOrdinal为7,即第7位,这将如何获得标志;因为 7 表示为 00000111。如果_nTestByte0b01000000,结果会是0?
    • @WillieScholtz OP 声明“一个字节的 8 位代表 8 个布尔“决策”(又名:标志)”。所以从我的角度来看,他需要测试_nBitOrdinal 值,例如124 等等。换句话说,每个“标志”都应该是 2 的幂。然后我提出的解决方案可以正常工作。
    • @WillieScholtz BTW 在检查了其他答案(并更仔细地阅读了 OP)之后,我意识到需求似乎有所不同......而且我实际上不明白它是什么:为什么 _nTestByte 必须用二进制表示?为什么它的(表观)值与我在测试时找到的正确结果不匹配?其实我很困惑!
    • @WillieScholtz 最后我只是修改了我的答案以准确反映我对 OP 的理解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 2019-02-16
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    相关资源
    最近更新 更多