按正确的顺序复制位

时间:2019-01-30 09:14:01

标签: c# bitwise-operators bit-shift

为了解析非托管数据的二进制流,我必须重新创建数据类型。数据已压缩,这意味着我读取了2个字节,这些字节实际上代表6位byte值和10位short值。

我要做的就是将一个位序列从一个值复制到另一个。我知道源值和目标值的起始位和长度。到目前为止,我已经采取了两种方法来复制正确的位,但是以某种相反的顺序。

byte BitwiseCopy(short value, int sourceStartBit, int destStartBit, int bitCount)
{
    short result = 0;
    for (int i = 0; i < bitCount; i++)
        //result |= (byte) (value & (1 << sourceStartBit + i) | (result & (1 << (destStartBit + bitCount) - i)));
        result |= (short) (value & (1 << sourceStartBit + i));

    return (byte) (result >> ((destStartBit - bitCount) + sizeof(byte) * 8));
}

对于我的测试场景,我使用具有以下值的短裤:

0000 0000 1101 0011
^15               ^0

我的目标是将此short的第4个-第7个位复制到字节的 0-3rd 位。 / p>

当我使用注释行(在return子句中没有代码)方法或当前突出显示的方法时,总是会得到以下结果:

0000 1011
^7      ^0

所以我想要的只是反转。我敢肯定它很小,但是我在这里俯瞰什么?我不明白为什么它会颠倒顺序。移位方法(直接按位复制并将其移位到正确的位置)不应该反转它吗?

编辑: 方法始终具有类型为short的输入。我有3个参数: sourceStart 是我从输入值(从低到高)开始复制的位, destStart 是我要复制到目标的位(可以是字节,也可以是短字节-为此,我将使用两种特定的方法)和 bitCount ,这是我要复制的位数(从低到高的顺序)。

方法必须以正确的顺序复制位。因此,例如,CopyBitwise(input, 4, 0, 4)应该返回(左:高,右:低阶)0000 1011,输入如下:

input [short]: ... 1011 0110
                   ^8th    ^0th

另一个:

input [short]: 1011 0110 0100 0111
               ^15th             ^0th
                    ^end ^start

CopyBitwise(input, 7, 3, 5)应该导致

0011 0000
^8th    ^0th
^end ^start

3 个答案:

答案 0 :(得分:1)

您不需要为此循环! 这应该可以解决问题:

byte BitwiseCopy(short value, int sourceStartBit, int destStartBit, int bitCount){
    byte result = (byte) ((value >> sourceStartBit) << destStartBit);
    result &= (byte) ~(0xff << bitCount); // mask for zeros at the left of result
    return result;
}

答案 1 :(得分:0)

听起来您想要的是剪切给定短裤的子序列。这可以通过 masking 来实现。如果要从以下位序列中剪切突出显示的位:1011 0110 0100 0111,则可以这样做通过&0000 0000 1111 0000来实现。

  1011 0110 0100 0111
& 0000 0000 1111 0000
= 0000 0000 0100 0000

然后可以移位结果位,以使被屏蔽的位从所需位置(此处为1)开始

  0000 0000 0100 0000 >>
 · 0000 0000 0100 000 >>
 ·· 0000 0000 0100 00 >>
 ··· 0000 0000 0100 0

为了更加具体,下面是一些代码:

byte BitwiseCopy(short value, int sourceStartBit, int destStartBit, int bitCount)
{
    ushort mask = 0; //Start with 0
    mask = (ushort)~mask; //Invert to get all 1s
    mask >>= sizeof(ushort)*8 - bitCount; //Shift right, until we have bitCount bits left
    mask <<= sourceStartBit; //Shift back left, to make sure the bits are in the right place

    ushort result = (ushort)value;
    result &= mask; //Mask out the bits we want

    //Shift the remaining bits into the desired position
    if (sourceStartBit < destStartBit)
    {
        result <<= destStartBit - sourceStartBit;
    }
    else
    {
        result >>= sourceStartBit - destStartBit;
    }
    return (byte)result;
}

代码细分:

首先,我们组装位掩码。为此,我们从0开始,然后将所有位取反以得到所有位。我们可以从0xFFFF开始,但是通过这种方式,如果您切换到不同长度的输入,代码仍然可以工作。

然后,我们将蒙版移动到仅剩bitCount 1个。对于此步骤,重要的是我们的掩码是 unsigned 整数类型。如果您使用的是带符号的值,则位移会从左侧向左移动新的1

要完成我们的遮罩,我们将其向左移以正确定位。

接下来,我们将掩码应用于我们的值以切出不需要的位。

最后,我们将结果移到所需位置;在这里再次重要的是使用 unsigned 移位,以确保没有错误的1从左侧移位。


这是使用参数调用方法时发生的示例 (0b1011_0110_0100_0111, 7, 3, 5),如您的第二个示例。

mask:
  0000 0000 0000 0000  ~x
= 1111 1111 1111 1111  x>>(16 - 5)
= 0000 0000 0001 1111  x<<7
= 0000 1111 1000 0000

result:
  1011 0110 0100 0111  x&mask
  0000 0110 0000 0000  x>>(7-3)
  0000 0000 0110 0000

编辑1: 将代码中的此行mask >>= bitCount - sizeof(ushort)*8;更正为mask >>= sizeof(ushort)*8 - bitCount;

编辑2: 我也弄乱了下面的行,也修复了该行。这就是我仅测试一个案例的结果。

编辑3: 添加了分步示例。

答案 2 :(得分:0)

这应该正常工作。 首先为所需的位创建掩码,然后将其移至所需位置。请注意,由于移位运算符必须为正数,所以要进行两次移动。

byte BitwiseCopy(short value, int sourceStartBit, int destStartBit, int bitCount)
{
  short mask = (short)((0xFFFF >> (16-bitCount)) << sourceStartBit);
  short result = (short)(((value & mask) >> sourceStartBit) << destStartBit);
  return (byte)result;
}