在 JavaScript 中创建可变宽度位掩码(0-32 位)
Create variable width bitmasks (0-32 bits) in JavaScript
我正在寻找一种在 JavaScript 中生成宽度从 0 到 32 位的位掩码的方法。这必须适用于从 0 到 32 的所有宽度,因此:
bitmask(0)
应该 return 0
二进制(十进制 0
);
bitmask(2)
应该 return 11
二进制(十进制 3
);
bitmask(5)
应该 return 11111
二进制(十进制 31
);
bitmask(32)
应该 return 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
问题
在JavaScript中,按位运算符将操作数视为 32 位整数。来自 the MDN Documentation:
The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.
这意味着用31位来表示实际的数字,用1位(最左边)来表示符号。所以尝试做类似 x << 31
的事情会导致溢出并产生错误的结果。
解决方法
您应该考虑使用不同的方法,例如 Math.pow()
,以获得更长的位掩码(和更高的值)。这是一个例子:
function bitmask(width) {
return Math.pow(2, width) - 1;
}
现在,假设 JavaScript 使用 IEEE Standard 754 浮点表示法,此函数也有限制:它仅适用于短于 54 位的位掩码。例如,bitmask(54)
产生的整数比正确的高一个单位,bitmask(55)
产生的整数比正确的高三个单位,依此类推,错误会随着位掩码宽度的增加而增大。
另请注意,即使此函数可以生成长于 31 位的位掩码,但由于上述原因,这些位掩码仍不能与按位运算符一起使用。
使用 Math.pow()
而不是移位。这将起作用,因为 Javascript 中的所有数字都是 64 位 "double" 精度 IEE754 浮点数。
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);
鉴于表达式 -1 >>> (32 - width)
适用于输入宽度 1–32,另一种可能的解决方案是表达式 width && -1 >>> 32 - width
,它适用于整个输入范围。
虽然比接受的答案更难阅读,但这个表达式还有其他好处:
- 它避免了函数调用(对
Math.pow
)
- 可以用更少的字符来写(为了打代码):
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));
}
我正在寻找一种在 JavaScript 中生成宽度从 0 到 32 位的位掩码的方法。这必须适用于从 0 到 32 的所有宽度,因此:
bitmask(0)
应该 return0
二进制(十进制0
);bitmask(2)
应该 return11
二进制(十进制3
);bitmask(5)
应该 return11111
二进制(十进制31
);bitmask(32)
应该 return11111111111111111111111111111111
二进制(十进制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
问题
在JavaScript中,按位运算符将操作数视为 32 位整数。来自 the MDN Documentation:
The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.
这意味着用31位来表示实际的数字,用1位(最左边)来表示符号。所以尝试做类似 x << 31
的事情会导致溢出并产生错误的结果。
解决方法
您应该考虑使用不同的方法,例如 Math.pow()
,以获得更长的位掩码(和更高的值)。这是一个例子:
function bitmask(width) {
return Math.pow(2, width) - 1;
}
现在,假设 JavaScript 使用 IEEE Standard 754 浮点表示法,此函数也有限制:它仅适用于短于 54 位的位掩码。例如,bitmask(54)
产生的整数比正确的高一个单位,bitmask(55)
产生的整数比正确的高三个单位,依此类推,错误会随着位掩码宽度的增加而增大。
另请注意,即使此函数可以生成长于 31 位的位掩码,但由于上述原因,这些位掩码仍不能与按位运算符一起使用。
使用 Math.pow()
而不是移位。这将起作用,因为 Javascript 中的所有数字都是 64 位 "double" 精度 IEE754 浮点数。
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);
鉴于表达式 -1 >>> (32 - width)
适用于输入宽度 1–32,另一种可能的解决方案是表达式 width && -1 >>> 32 - width
,它适用于整个输入范围。
虽然比接受的答案更难阅读,但这个表达式还有其他好处:
- 它避免了函数调用(对
Math.pow
) - 可以用更少的字符来写(为了打代码):
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));
}