如何体面地进行位运算?

How to do bitwise operation decently?

我正在对二进制数据进行分析。假设我有两个 uint8 数据值:

a = uint8(0xAB);
b = uint8(0xCD);

我想从 a 中取出较低的两位,并从 b 中取出全部内容来生成一个 10 位的值。在 C 风格中,它应该是这样的:

(a[2:1] << 8) | b

我试过了bitget:

bitget(a,2:-1:1)

但这只是给了我单独的[1, 1]逻辑类型值,它不是标量,不能在后面的bitshift操作中使用。

我目前的解决方案是:

  1. 使a|bab):

    temp1 = bitor(bitshift(uint16(a), 8), uint16(b));

  2. 左移六位去掉a的高六位:

    temp2 = bitshift(temp1, 6);

  3. 右移六位以去除先前结果中的低位零:

    temp3 = bitshift(temp2, -6);

将所有这些放在一行中:

result = bitshift(bitshift(bitor(bitshift(uint16(a), 8), uint16(b)), 6), -6);

这似乎效率不高,对吧?我只想得到(a[2:1] << 8) | b,需要很长的表达式才能得到值

如果这个问题有众所周知的解决方案,请告诉我。

or在处理整数时相当于加法

result = bitshift(bi2de(bitget(a,1:2)),8) + b;

例如

a = 01010111
b = 10010010

result =       00000011            100010010
       = a[2]*2^9 + a[1]*2^8   +       b

另一种方法可以是

result = mod(a,2^x)*2^y + b;

其中 x 是要从 a 中提取的位数,yab 的位数,在你的情况下:

result = mod(a,4)*256 + b;

接近C解决方案的额外替代解决方案:

result = bitor(bitshift(bitand(a,3), 8), b);

由于您使用的是 Octave,因此可以使用 bitpackbitunpack:

octave> a = bitunpack (uint8 (0xAB))
a =

   1   1   0   1   0   1   0   1

octave> B = bitunpack (uint8 (0xCD))
B =

   1   0   1   1   0   0   1   1

一旦你以这种形式获得它们,你就可以轻而易举地做你想做的事了:

octave> [B A(1:2)]
ans =

   1   0   1   1   0   0   1   1   1   1

然后只需相应地用零填充并将其打包回整数:

octave> postpad ([B A(1:2)], 16, false)
ans =

   1   0   1   1   0   0   1   1   1   1   0   0   0   0   0   0

octave> bitpack (ans, "uint16")
ans = 973

我认为准确解释“(a[2:1] << 8) | b”在做什么很重要。

在汇编中,引用单个位是一个单一的操作。假设所有操作都花费完全相同的时间并且 "efficient" a[2:1] 开始看起来效率极低。

便利语句实际上是 (a & 0x03)。

如果您的编译器实际上根据移位量将 uint8 转换为 uint16,这本身不是 'free' 操作。实际上,您的编译器将首先将 "memory" 清除为 uint16 的大小,然后将 "a" 复制到该位置。这需要通常不需要的额外步骤(清除 "memory"(寄存器))。

这意味着你的声明实际上是 (uint16(a & 0x03) << 8) | uint16(b)

现在是的,因为你正在做二次方移位,你可以将 a 移到 AH,将 b 移到 AL,然后将 AH 移到 0x03,然后将其全部移出,但这是编译器优化,而不是你的C代码说到做到。

关键是直接将该语句转换为 matlab 产量

bitor(bitshift(uint16(bitand(a,3)),8),uint16(b))

但是,应该注意的是虽然它不像 (a[2:1] << 8) | 那样简洁b、"high level operations"个数相同

请注意,所有脚本语言在启动每条指令时都会非常慢,但会快速完成所述指令。 Python 的简洁性不是因为 "terse is better" 而是为了创建语言可以识别的简单结构,以便它可以轻松进入矢量化操作模式并非常快速地开始执行代码。

这里的重点是调用 bitand 需要 "overhead" 成本;但是在数组上操作时,它将使用 SSE,并且 "overhead" 只支付一次。 JIT(即时)编译器,它通过减少开销调用和为当前执行的代码部分创建临时机器代码来优化脚本语言,可以识别出对按位操作链的类型检查只需要在初始输入上发生,从而进一步减少运行时间。

非常高级的语言与高级语言(例如 C)完全不同(并且令人沮丧)。为了便于代码生成,您放弃了对代码执行的大量控制; matlab 是否真的实现了 uint8 或者它是否真的在使用 double 并截断它,你不知道。对原生 uint8 的按位运算非常快,但是从 float 转换为 uint8,执行按位运算,然后再转换回来就很慢了。 (从历史上看,Matlab 对所有内容都使用双精度值,并且仅根据您指定的 'type' 四舍五入)

即使是现在,Octave 4.0.3 也有一个已编译的移位函数,对于 bitshift(ones('uint32'),-32) 会导致它返回到 1。太棒了! VHLL 使您受制于语言,这与您编写的代码有多简洁或多冗长无关,而是该死的语言决定解释它并执行机器级代码的方式。因此,uint32(floor(ones / (2^32))) 实际上更快更准确,而不是移动。