如何在 JavaScript 中添加位

How to add bits in JavaScript

假设您有两个整数 10 和 20。即 0000101000010100。然后我想基本上将它们连接为字符串,但结果是一个新的整数。

00001010 + 00010100 == 0000101000010100

最后的数字是 2580

但是,我正在寻找一种无需将它们实际转换为字符串即可执行此操作的方法。寻找更有效的东西,只是对整数本身进行一些调整。我不太熟悉,但我想应该是这样的:

var a = 00001010 // == 10
var b = 00010100 // == 20
var c = a << b //   == 2580

请注意,我希望它适用于任何位序列。所以偶:

var a = 010101
var b = 01110
var c = a + b == 01010101110

你的基本等式是:

c = b + (a << 8).

这里的诀窍是你需要总是移动 8。但是由于 ab 并不总是使用字节中的所有 8 位,所以 JavaScript 将自动省略任何前导零。我们需要恢复前导零(b)的数量,或 a 的尾随零的数量,并在添加之前将它们添加回去。这样,所有位都保持在正确的位置。这需要这样的等式:

c = b + (a << s + r)

其中sb中设置的最高位(从右到左),rs + r = 8剩余的位数。

本质上,您所做的只是将第一个操作数 a 移动 8 位,以有效地将尾随零添加到 a 或同样地说,将前导零填充到第二个操作数 b。然后你正常添加。这可以通过使用对数、移位和按位 OR 运算来为某些任意 正整数 a 和 [= 提供 O(1) 解决方案15=] 其中 ab 中的位数不超过某个正整数 n。在字节的情况下,n = 8.

// Bitwise log base 2 in O(1) time
function log2(n) {

  // Check if n > 0

  let bits = 0;

  if (n > 0xffff) {
      n >>= 16;
      bits = 0x10;
  }

  if (n > 0xff) {
      n >>= 8;
      bits |= 0x8;
  }

  if (n > 0xf) {
      n >>= 4;
      bits |= 0x4;
  }

  if (n > 0x3) {
      n >>= 2;
      bits |= 0x2;
  }

  if (n > 0x1) {
      bits |= 0x1;
  }

  return bits;
}

// Computes the max set bit
// counting from the right to left starting
// at 0. For 20 (10100) we get bit # 4.
function msb(n) {

  n |= n >> 1;
  n |= n >> 2;
  n |= n >> 4;
  n |= n >> 8;
  n |= n >> 16;

  n = n + 1;

  // We take the log here because
  // n would otherwise be the largest
  // magnitude of base 2. So, for 20,
  // n+1 would be 16. Which, to 
  // find the number of bits to shift, we must 
  // take the log base 2
  return log2(n >> 1);
}

// Operands
let a = 0b00001010  // 10
let b = 0b00010100  // 20

// Max number of bits in
// in binary number
let n = 8

// Max set bit is the 16 bit, which is in position
// 4. We will need to pad 4 more zeros
let s = msb(b) 

// How many zeros to pad on the left
// 8 - 4 = 4
let r = Math.abs(n - s)

// Shift a over by the computed 
// number of bits including padded zeros
let c = b + (a << s + r)

console.log(c)

输出:

2580

备注:

  • 这不是可交换的。
  • 为 log2() 添加负数和其他边缘情况的错误检查。

参考文献:

Note, I would like for this to work with any sequences of bits. So even:

var a = 010101
var b = 01110
var c = a + b == 01010101110

除非您转换为字符串或以其他方式存储每个数字中的位数,否则这是不可能的。 10101 010101 0010101 等都是相同的数字(21),一旦转换为数字,就无法判断数字最初有多少个前导零。

所以问题:
a 为 10(二进制 0000 1010)
b 是 20(二进制 0100 0100)
您想以某种方式使用位移位获得 2580。

如果您使用 a<<=8 将 a 右移 8(这与将 a 乘以 2^8 相同),您将得到
1010 0000 0000,这与 10*2^ 相同8 = 2560.
因为 a 的低位都是 0(当你使用 << 时它用 0 填充新位)你可以在它上面添加 b 1010 0000 0000 + 0100 0100 给你 1010 0001 0100.

所以在 1 行代码中,它是 var result = a<<8 + b

请记住,在编程语言中,大多数语言都没有 "binary" 的显式内置类型。但一切在本质上都是二元的。所以 int 是 "binary",对象是 "binary" ....等等。当你想对某些数据进行一些二进制操作时,你可以只使用你拥有的数据类型作为二进制操作的操作数。

这是一个更通用的版本,说明如何不使用字符串操作和数据

来连接两个数字的二进制表示

/*
  
  This function concate b to the end of a and put 0's in between them.  
  
  b will be treated starting with it's first 1 as its most significant bit
  
  b needs to be bigger than 0, otherwise, Math.log2 will give -Infinity for 0 and NaN for negative b
  
  padding is the number of 0's to add at the end of a
*/

function concate_bits(a, b, padding) {

  //add the padding 0's to a
  a <<= padding;

  //this gets the largest power of 2
  var power_of_2 = Math.floor(Math.log2(b));
  var power_of_2_value;

  while (power_of_2 >= 0) {
    power_of_2_value = 2 ** power_of_2;
    a <<= 1;
    if (b >= power_of_2_value) {
      a += 1;
      b -= power_of_2_value;
    }
    power_of_2--;
  }
  return a;
}

//this will print 2580 as the result
let result = concate_bits(10, 20, 3);
console.log(result);