【问题标题】:Diffing the bits of an integer区分整数的位
【发布时间】:2016-05-04 08:38:07
【问题描述】:

我有一个 32 位整数,我通过将各个位映射到各种类型的数据来填充数据。

例如,一个部分用于氧气水平。这是一个从 0 到 128 的值,所以我使用 7 位。

另一个部分用于对象的旋转。围绕三个轴 x、y 和 z 旋转始终为 0、90、180 或 270 度。每个角度的索引值为 0、1、2 和 3,所以我只需要 6 位。旋转存储为 010101,表示围绕 x、y 和 z 旋转 90 度,或 100000 表示围绕 x 旋转 180 度。

某些部分存储为整数值,例如氧气水平,但在我需要使用它们时转换为枚举。

每个部分在数据整数中彼此相邻排列,从右到左填充位。

Type :    ... | Oxygen  | Rotation |
          --------------------------
Bits:     ... | 0000000 | 000000   |
          --------------------------
Position: ... | 19-25   | 26-31    |
          --------------------------

问题

我想比较两个整数,看看有什么变化。

当一个int的氧含量为16,另一个为20时,相差增加4个单位。当一个旋转在所有轴上为 0,另一个是围绕 x 旋转 90 时,差异是围绕 x 旋转 90 度。

我在数据对象上有扩展方法,允许我将氧气水平作为 int 值,将旋转作为四元数。当在 delta 数据对象上使用这些时,我想获得氧气水平的值 4,以及围绕 x 旋转 90 度的四元数。

问题

获取两个整数值差的最有效方法是什么?

可能的方法

我想了几种不同的方法来解决这个问题。

整数比较

由于数据是一个整数,我试图简单地从另一个中减去一个。我从一个值为 0 的 int 开始,并将氧水平的位设置为 16。我创建了一个新的 int,将其氧水平设置为 20,然后从最后一个减去第一个,增量的氧水平为 4 . 但是当我向整数添加旋转和其他数据时,减法后的结果改变了产生的氧气水平和旋转。

我需要验证我的所有扩展方法都按预期工作。所有单元测试都成功,但测试可能不够好。

按位比较

另一种方法是使用各种位运算符分别比较整个整数中的每个位。我使用了一个 int,其旋转位设置为围绕 x 90 度(位 01)。另一个 int 将 x 旋转设置为 180 度(位 10)。我正在寻找的 delta 值为 90 度(位 01),但我找不到任何合适的运算符来产生该结果。

这个测试让我相信我不能单独比较位。在旋转索引的情况下,我需要将一对位视为单个值。

位数组

我阅读了 .net 的 BitArray 的文档,但无法立即看出使用它会有什么不同。

比较每个部分

比较每个位部分会产生我正在寻找的结果。在计算差值之前,旋转位将转换为四元数,然后在将它们设置为数据整数之前转换回位。

这是我希望找到替代方法的方法。我不想让比较器知道数据的结构,我希望找到更有效的解决方案。

编辑

我现在看到,在阅读了 cmets 并进行了更多测试之后,对于我的案例来说,仅获得位部分的新值而不是它们改变了多少对我来说更有利。任何保持不变的部分都应归零。据我所知,我唯一的选择是分段比较数据。

再次编辑

我不确定选择哪个答案作为正确答案。我认为我的问题不够具体,对此我深表歉意。

@harold 给出的方法在逐位比较时有效,并且与数据结构无关。

@Pikoh 的回答逐节比较,并且可以动态化,这样该方法就不需要了解数据结构。

【问题讨论】:

  • 如果减法不是您想要的,“区别”是什么?检测两个整数 不同是微不足道的。如果您想要“差异”,则需要先定义此操作。仅仅说明如何布置数据是不够的。 (听起来您希望不同部分有多个“差异”。)
  • “差异”不足以告诉我们您想要什么。您需要告诉我们这种差异比较的预期输出。这在您问题的这一部分中也很明显:“没有产生我想要的结果”。您还没有告诉我们您在寻找什么,所以我们无法帮助您。
  • 听起来第二个选项是最直接的。您可以发布您为此尝试过的代码吗?
  • 对于您的旋转,您可能需要考虑不同的编码 - 0° -> 00、90° -> 01、180° -> 11 和 270° -> 10。这样,只有相隔 90° 的角度之间的一个位差。
  • 如果您只想要新值,您可以简单地将旧值与新值异或,然后将结果与新值异或。所以只有改变的位在结果中,其他位全部清零。

标签: c# integer bit-manipulation


【解决方案1】:

我不确定您想要什么,但使用典型的SWAR techniques 计算所有字段的模差当然是可能的:

z = ((x | H) - (y &~H)) ^ ((x ^~y) & H)

这是 SWAP 减法的通用公式。对于 2 位字段,H = 0xAAAAAAAA

由于只有两个位,并且 SWAR 通常以不同的方式处理最高位(以防止泄漏到下一个字段中),因此这些位实际上是完全分开的。


根据新要求,也没有必要对字段进行难看的拆分,例如:(未测试)

m = x ^ y;
m = (m | (m >> 1)) & 0x55555555;
m *= 3;
z = y & m; // maybe

这里的想法是,一个异或将在一个字段的某处产生一个 1 如果它已经改变,然后将该字段的所有位进行 OR 并将其放在该字段的最低位,乘以 3 将它广播到所有位领域的。 & 使用新值在其他地方获取更改的字段和零,但这意味着您无法区分“更改为 0”和“未更改”。使用m,您仍然可以区分它们。

【讨论】:

  • 感谢您提供有关 SWAR 技术的提示。我会看看它。我不太明白您在答案的第二部分中执行的步骤,但从一些测试来看,它似乎正在工作。
  • 我做了更多的测试,得到了一些奇怪的结果。我从您的回答中看到您尚未对其进行测试,这很好。当 x=2 和 y=6 时,我得到 4 作为结果。当 x=7 和 y=6 时,我得到 2。
  • @ThomasViktil 但没关系,对吧?例如对于 x=7, y=6,有一个字段发生了变化,即最低的字段,从 3 变为 2,该字段的新值保持不变,其他字段为零
  • 当然,你是对的!一点一点比较,你的方法是有效的。
【解决方案2】:

让我们看看这样的事情是否对你有帮助:

int data1 = Convert.ToInt32("00000000000000000000000000011100", 2); //sample data
int data2 = Convert.ToInt32("00000000000000000000000000101000", 2);
int xrotationposition = 26; //start positions of data
int yrotationposition = 28;

string xrotationmask = new string('0', xrotationposition) + "11" + 
                   new string('0',30 - xrotationposition); //mask to extract the data
string yrotationmask = new string('0', yrotationposition) + "11" + 
                   new string('0', 30 - yrotationposition);

int xrotation1 = data1 & Convert.ToInt32(xrotationmask, 2); //bit AND
int xrotation2 = data2 & Convert.ToInt32(xrotationmask, 2);

int yrotation1 = data1 & Convert.ToInt32(yrotationmask, 2);
int yrotation2 = data2 & Convert.ToInt32(yrotationmask, 2);

xrotation1 = (xrotation1 >> 30 - xrotationposition); //shift the bits 
                                                     to the lowest part of byte
xrotation2 = (xrotation2 >> 30 - xrotationposition);

yrotation1 = (yrotation1 >> 30 - yrotationposition);
yrotation2 = (yrotation2 >> 30 - yrotationposition);

在此执行结束时,您将获得xrotation1=1xrotation2=2yrotation1=3yrotation2=2,这似乎很容易比较值。

希望对你有帮助

【讨论】:

  • 我明白你的所作所为。尽管您提供了一个可行的解决方案,但我一直在寻找一个差异方法不需要了解数据结构的解决方案。但是,您的解决方案给了我如何解决这部分挑战的想法。通过将掩码和偏移量放入数组中,我可以使用 foreach 将掩码与按位与应用于源,并偏移结果以获取数据。无论如何,我需要回到绘图板上重新迎接挑战。
猜你喜欢
  • 2023-04-09
  • 1970-01-01
  • 2012-05-10
  • 1970-01-01
  • 2013-09-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
相关资源
最近更新 更多