在JavaScript中创建可变宽度的位掩码(0-32位)。

4
我正在寻找一种方法来在JavaScript中生成不同宽度的位掩码,从0到32位。这必须适用于所有从0到32的宽度,以便:
- bitmask(0)应以二进制返回0(十进制0); - bitmask(2)应以二进制返回11(十进制3); - bitmask(5)应以二进制返回11111(十进制31); - bitmask(32)应以二进制返回11111111111111111111111111111111(十进制4294967295 [uint32]或-1 [int32])。
表达式(1 << width) - 1 对于0到30位的宽度有效。
是否有一个简单的表达式(最好没有if / else分支),可以用于实现输入范围(0-32)的所有值?
以下测试代码说明了我的当前方法如何失败31和32:

function bitmask(width) {
  return (1 << width) - 1;
}

function test(width){
  var result = bitmask(width).toString(2);
  console.log(width, 
              result, 
              result.replace(/[^1]/g,'').length === width? 'Ok': 'Fail');
}

test(0);
test(1);
test(5);
test(17);
test(30);
test(31); // fails
test(32); // fails


我认为你刚刚触及了JS本身的硬性限制。 "位运算符将其操作数视为32位序列..." https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators - Sharky
3个回答

2
考虑到表达式-1 >>> (32 - width)适用于输入宽度为1-32,另一个可能的解决方案是表达式width && -1 >>> 32 - width,它适用于所有输入范围。
虽然比被接受的答案更难阅读,但这个表达式还有其他好处:
  1. 它避免了函数调用(到Math.pow
  2. 它可以用更少的字符编写(用于代码打高尔夫):
    • w&&-1>>>32-w(12个字符),相对于
    • Math.pow(2,w)-1(15个字符)
演示代码:

function bitmask(width) {
  return width && -1 >>> 32 - width;
}

// demonstrate that it works

for(var i = 0; i <= 32; i++) {
  console.log(i, bitmask(i).toString(2));
}


2

问题

在JavaScript中,位运算符将操作数视为32位整数。从MDN文档中了解到:

所有位运算符的操作数都会被转换为用二进制补码表示的32位有符号整数。

这意味着31个比特位用于表示实际数字,1个比特位(最左边)用于表示符号。因此,尝试执行类似 x << 31 的操作将导致溢出并产生错误结果。

解决方法

您应该考虑使用其他方法,例如Math.pow(),来获取更长的位掩码(和更高的值)。以下是一个示例:

function bitmask(width) {
    return Math.pow(2, width) - 1;
}

考虑到JavaScript使用IEEE标准754浮点表示,因此该函数也存在限制:它仅适用于比54位短的位掩码。例如,bitmask(54)生成的整数比正确值高一单位,bitmask(55)生成的整数比正确值高三个单位,随着位掩码宽度的增加,误差不断增加。

此外,请注意,即使此函数可以生成长达31位的位掩码,这些位掩码仍无法与位运算符一起使用,原因如上所述。


简单的表达,非常好的回答。谢谢。 - Tomas Langkaas

1
使用 Math.pow() 替代位移操作。这是可行的,因为 Javascript 中的所有数字都是 64 位的“双精度” IEEE754 浮点数。

function bitmask(width) {
  return Math.pow(2, width) - 1;
}

function test(width){
  var result = bitmask(width).toString(2);
  console.log(width, 
              result, 
              result.replace(/[^1]/g,'').length === width? 'Ok': 'Fail');
}

test(0);
test(1);
test(5);
test(17);
test(30);
test(31);
test(32);


很好的解决方案,和Marco Bonelli(https://dev59.com/V5vga4cB1Zd3GeqPvgZs#39660712)是一样的。谢谢。 - Tomas Langkaas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接