【问题标题】:Join bits using bitwise operations. Is this even possible?使用按位运算连接位。这甚至可能吗?
【发布时间】:2014-11-19 11:20:19
【问题描述】:

我要问的是是否可以将所有位加入 2 个不同的数字

伪代码示例:

bytes=array(0x04, 0x3F);

//place bitwise black magic here

print 0x043F;

另一个例子:

bytes=array(0xFF, 0xFFFF);

//place bitwise black magic here

print 0xFFFFFF;

又一个例子:

bytes=array(0x34F3, 0x54FD);

//place bitwise black magic here

print 0x34F354FD;

我想将其限制为仅按位运算符>><<|^~&)。

这应该至少在 PHP 和 Javascript 中有效。

这有可能吗?

如果我不清楚,请在评论中提出您的疑问。

【问题讨论】:

  • @harold 忘了那个。我会添加它。
  • @harold 还添加了~,从一开始就应该存在。

标签: language-agnostic programming-languages bit-manipulation


【解决方案1】:

如果我正确理解您的问题,
这应该是php中的答案:

    $temp = $your_first_value << strlen(dechex($the_length_of_the_second_value_in_hex))
    $result = $temp | $your_second_value
    print dechex($result)

更新:而不是 + 使用 |运营商

【讨论】:

  • 确实使用了加法 (`+´) 运算符。我只指定了按位运算符。我进行了编辑以突出显示该部分并列出了要使用的运算符。但你确实提出了一个非常好的解决方案!
  • 实际上,$temp = $your_first_value &lt;&lt; $the_length_of_the_second_value_in_hex 位将是$temp = $your_first_value &lt;&lt; 8。像这样,它适用于任意整数(低于 32 位)。
  • 你的答案现在和 uliwitness 的答案完全一样,也有同样的问题。如果 $your_second_value 被限制在 0 到 255 之间,这非常有用。假设只有 1 个字节。我要问的是任意数字(引用:What I am asking is if it is possible to join all bits in 2 different numbers)。
  • 我又添加了 2 个我假装的例子,以阐明我的期望。
  • 所以唯一的问题是解决第二个十六进制值的长度,然后一切都应该没问题吧?
【解决方案2】:

这个问题完全取决于能否确定整数中最左边的 1 的位置。一种方法是“正确涂抹位”,然后计算 1:

向右涂抹:

int smearright(int x) {
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return x;
}

简单,只有位运算符。然而,计算位数涉及某种加法:

int popcnt(int x) {
    x = add(x & 0x55555555, (x >> 1) & 0x55555555);
    x = add(x & 0x33333333, (x >> 2) & 0x33333333);
    x = add(x & 0x0f0f0f0f, (x >> 4) & 0x0f0f0f0f);
    x = add(x & 0x00ff00ff, (x >> 8) & 0x00ff00ff);
    x = add(x & 0xffff, (x >> 16) & 0xffff);
    return x;
}

不过没关系,add可以实现为

int add(int x, int y) {
    int p = x ^ y;
    int g = x & y;
    g |= p & (g << 1);
    p &= p << 1;
    g |= p & (g << 2);
    p &= p << 2;
    g |= p & (g << 4);
    p &= p << 4;    
    g |= p & (g << 8);
    p &= p << 8;
    g |= p & (g << 16);
    return x ^ y ^ (g << 1);
}

把它放在一起:

join = (left << popcnt(smearright(right))) | right;

如果你有加法(没有add 函数)显然要容易得多,但也许令人惊讶的是,它甚至比乘法更简单:

join = (left * (smearright(right) + 1)) | right;

再也没有popcnt了!

用位运算符实现乘法不会有帮助,更糟糕的是,我不确定你甚至可以用列出的运算符来做到这一点(除非右移是算术移位,但它仍然是一件可怕的事情涉及 32 个加法,每个加法本身都是函数)。

此答案中没有“偷偷摸摸的技巧”,例如使用隐式测试与零相等的条件(if?:while 等中的“隐藏”!= 0),以及控制流实际上是完全线性的(函数调用只是为了防止重复代码,一切都可以内联)。


这里有一个替代方案。不要使用popcnt,而是做一个奇怪的变量移位:

int shift_by_mask(int x, int mask) {
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    return x;
}

好吧,这并不让我高兴,但这是你如何使用它:

join = shift_by_mask(left, smearright(right)) | right;

【讨论】:

  • 这是一个与语言无关的问题,您似乎只给出了一个 c 答案。 Javascript 和 PHP 无法使用它。 PHP 甚至没有无符号数!
  • @IsmaelMiguel 我这样做是为了使正确的转变成为逻辑转变,经过审查,事实证明这是不必要的。顺便说一句,它不应该是 C,我在写它的时候就想到了 C#(当然,逻辑右移并不是 C# 独有的)
  • pastebin.com/3xYQthgs 这里是将您的代码重写为 1 个函数。它满足所有要求和工作!你可以在这里测试:writecodeonline.com/php(它使用了 2 个do{}while 循环,还不错)。
【解决方案3】:

根据您的机器的字节序,您可能需要颠倒下面的 bytes[0] 和 bytes1 的顺序:

uint8_t  bytes[2] = { 0x04, 0x3f };
uint16_t result = (bytes[0] << 8) | bytes[1];

(这是C语言,翻译成PHP等应该不难,语言和运算符都差不多)

更新: 好的,既然你已经澄清了你想要什么,基本的方法还是一样的。相反,您可以做的是计算正确数字中的位数,然后像上面一样在左侧数字上进行位移,只是使用动态位数。只要您的位数不超过您的语言/平台支持的最大数字类型(因此在本例中为 64 位),这将起作用。

int  rightMaxBits = 0;
uint64_t leftNum = 0x04, rightNum = 0x3f;
uint64_t rightNumCopy = rightNum;

while( rightNumCopy )
{
    rightNumCopy >>= 1;
    rightMaxBits++;
}

uint64_t resultNum = (leftNum << rightMaxBits) | rightNum;

(感谢this SO thread 的位计数算法)对于签名号码,我建议您在调用此号码之前在号码上使用abs(),然后以您想要的任何方式重新应用签名。

【讨论】:

  • 您的解决方案假定这些是字节。我要问的是 2 个数字(引用:What I am asking is if it is possible to join all bits in 2 different numbers)。不同之处在于它不仅限于字节。如果第二个数字介于 0 和 255 之间,您的解决方案效果很好。
  • 我又添加了 2 个我假装的例子,以阐明我的期望。
  • 如果不是单字节也是一样的。只需根据您的数字有多少位调整数据类型和位移量。
  • 但是数字可以是负数、正数、32 位、64 位、0、-1(我对待这个不同)。您的解决方案需要事先知道您将使用什么类型。是的,这是一种方法,但它仅适用于bytes[1] 介于 0 (0x00) 和 255 (0xff) 之间的情况。另一个数字,如 256(0x1ff,按记忆编号)将返回虚假结果。
  • 嗯,你的问题不包括任何这些要求,所以我猜这不是你想要的。我所做的只是为您的原始问题提供解决方案。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-20
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-14
相关资源
最近更新 更多