使用按位运算连接位。这甚至可能吗?

Join bits using bitwise operations. Is this even possible?

提问人:Ismael Miguel 提问时间:11/19/2014 最后编辑:Ismael Miguel 更新时间:11/21/2014 访问量:381

问:

我要问的是是否可以将所有位连接成 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 中起作用。

这有可能吗?

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

与语言无关 的编程语言 位操作

评论

0赞 Ismael Miguel 11/19/2014
@harold 忘了那个。我会添加它。
0赞 Ismael Miguel 11/19/2014
@harold 还添加了 ,从一开始就应该有 ben。~

答:

1赞 Ehsan 11/19/2014 #1

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

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

更新:使用 |算子

评论

0赞 Ismael Miguel 11/19/2014
这确实使用加法 ('+') 运算符。我只指定了按位运算符。我做了一个编辑来突出显示那部分,并列出了要使用的运算符。但是您确实提出了一个非常好的解决方案!
0赞 Ismael Miguel 11/19/2014
实际上,位将是.像这样,它适用于任意整数(低于 32 位)。$temp = $your_first_value << $the_length_of_the_second_value_in_hex$temp = $your_first_value << 8
0赞 Ismael Miguel 11/19/2014
你的答案现在和 uliwitness 的答案一模一样,也有同样的问题。如果限制在 0 到 255 之间,则效果很好。这假设只有 1 个字节。我问的是任意数字(引用:数字)。$your_second_valueWhat I am asking is if it is possible to join all bits in 2 different
0赞 Ismael Miguel 11/19/2014
我又添加了 2 个我假装的例子,以澄清我的期望。
0赞 Ehsan 11/19/2014
所以唯一的问题是用十六进制解析第二个值的长度,然后一切都应该没问题吧?
0赞 uliwitness 11/19/2014 #2

根据计算机的字节序,可能需要颠倒下面的字节 [0] 和字节1 的顺序:

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;

(感谢此 SO 线程的位计数算法)对于有符号的号码,我建议您在拨打此号码之前使用该号码,然后以您想要的任何方式重新应用符号。abs()

评论

0赞 Ismael Miguel 11/19/2014
您的解决方案假定这些是字节。我要问的是 2 个数字(引用:数字)。不同之处在于它不仅限于字节。如果第二个数字介于 0 和 255 之间,则您的解决方案效果很好。What I am asking is if it is possible to join all bits in 2 different
0赞 Ismael Miguel 11/19/2014
我又添加了 2 个我假装的例子,以澄清我的期望。
0赞 uliwitness 11/20/2014
如果它们不是单个字节,则相同。只需根据数字的位数相应地调整数据类型和位移量即可。
0赞 Ismael Miguel 11/20/2014
但是数字可以是负数、正数、32 位、64 位、0、-1(我以不同的方式对待这个)。您的解决方案需要事先知道您将使用哪种类型。是的,这是一种方法,但它仅适用于介于 0 () 和 255 () 之间的情况。另一个数字,如 256(,按内存编号)将返回虚假结果。bytes[1]0x000xff0x1ff
0赞 uliwitness 11/20/2014
好吧,你的问题不包括任何这些要求,所以我没猜到这就是你想要的。我所做的只是为您最初的问题提供解决方案。:-)
1赞 harold 11/19/2014 #3

这个问题完全取决于能否确定整数中最左边的 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 个加法,每个加法都是函数本身)。

这个答案中没有“鬼鬼祟祟的伎俩”,比如使用隐式测试与零相等的条件(“隐藏”在 、 等中),控制流实际上是完全线性的(函数调用只是为了防止重复代码,一切都可以内联)。!= 0if?:while


这是另一种选择。而不是采取 ,做一个奇怪的变量移位: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;

评论

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