Language agnostic 锯齿形译码

Language agnostic 锯齿形译码,language-agnostic,bit-manipulation,protocol-buffers,bitfoo,zigzag-encoding,Language Agnostic,Bit Manipulation,Protocol Buffers,Bitfoo,Zigzag Encoding,在google协议缓冲区中,他们引入了一种称为“Z字型编码”的方法,这种方法采用大小较小的有符号数字,并创建一系列大小较小的无符号数字 比如说 Encoded => Plain 0 => 0 1 => -1 2 => 1 3 => -2 4 => 2 5 => -3 6 => 3 等等。他们为此提供的编码功能相当聪明,它是: (n << 1) ^ (n >> 31) //for a 32 bit integer (n>3

在google协议缓冲区中,他们引入了一种称为“Z字型编码”的方法,这种方法采用大小较小的有符号数字,并创建一系列大小较小的无符号数字

比如说

Encoded => Plain
0 => 0
1 => -1
2 => 1
3 => -2
4 => 2
5 => -3
6 => 3
等等。他们为此提供的编码功能相当聪明,它是:

(n << 1) ^ (n >> 31) //for a 32 bit integer
(n>31)//对于32位整数

我知道这是怎么回事,但是,我一辈子都不知道如何将其反转并解码成有符号的32位整数。我找到了一个解决方案,不幸的是,这不是我所希望的单线美:

uint signMask = u << 31;
int iSign = *((Int32*)&signMask);
iSign >>= 31;
signMask = *((UInt32*)&iSign);

UInt32 a = (u >> 1) ^ signMask;
return *((Int32*)&a);
uint signMask=u>=31;
标志掩码=*((UInt32*)和iSign);
UInt32 a=(u>>1)^标志掩码;
返回*((Int32*)&a);

我确信有一些超高效的按位操作可以更快地完成这项工作,但函数很简单。下面是一个python实现:

def decode(n):
  if (n < 0):
    return (2 * abs(n)) - 1
  else:
    return 2 * n

>>> [decode(n) for n in [0,-1,1,-2,2,-3,3,-4,4]]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
def解码(n):
如果(n<0):
返回(2*abs(n))-1
其他:
返回2*n
>>>[对[0,-1,1,-2,2,-3,3,-4,4]中的n进行解码(n)]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
怎么样

(n>>1) - (n&1)*n
试试这个:

(n >> 1) ^ (-(n & 1))
编辑:

我发布了一些示例代码以供验证:

#include <stdio.h>

int main()
{
  unsigned int n;
  int r;

  for(n = 0; n < 10; n++) {
    r = (n >> 1) ^ (-(n & 1));
    printf("%u => %d\n", n, r);
  }

  return 0;
}

这里还有另一种方法可以做到这一点,只是出于解释的目的(您显然应该使用3Electrologos’one liner)

您只需注意,您可以使用一个全为1(相当于按位not)或全为0(相当于不执行任何操作)的数字进行xor。这就是
((n&1))
产生的结果,也就是谷歌的“算术移位”评论所解释的

int-zigzag_到有符号(无符号int-zigzag)
{
int abs=(int)(之字形>>1);
如果(锯齿形%2)
返回~abs;
其他的
返回abs;
}
无符号整数符号_到_之字形(整数符号)
{

unsigned int abs=(unsigned int)signed在修改了3electrologos提出的公认答案后,我在开始使用unsigned long时无法使其工作(在C#——编译器错误中)。我想出了类似的方法:

( value >> 1 ) ^ ( ~( value & 1 ) + 1 )

这适用于任何在2的赞美语中表示负数的语言(例如.NET).

我知道必须有一种方法来处理乘法。太好了!它对我和ergosys都有效,所以它也应该对你有效…你能告诉我你得到了什么结果吗?很可能是我使用的不正确。我有一个UInt32表示n,然后我将返回的结果转换成Int32。这听起来是一种合乎逻辑的方法…我这可能是一个语言问题,将其直接翻译成C会导致错误,否定UInt32会导致long,并且long和UInt32的xor是未定义的。我将尝试为C#return((int)(u>>1))^((int)((u&1))修复它;施法的力量已经解决了这个问题。因此,问题仍然是我应该使用哪一个,根据ergosys上面所说的,我假设这是由于缺少乘法而更快?谢谢,但不幸的是,这是在一个游戏的网络编码系统中,这个特殊的解码功能在每个包中使用了很多次,每秒使用了很多次-这是必须的快速你可以使用一个简单的位运算来加快速度。移位1乘以2。-1:此函数将有符号数编码为编码无符号数。原始问题已经有一个函数可以这样做。原始问题要求一个函数将这些无符号数解码回原始有符号数:我们需要解码e(3)返回-2,但此函数使decode(3)返回6。
zigZag_to_signed
没有返回原始值。@SalarKhalilzadeh谢谢,修复了。运算符优先级错误,强制转换后丢失了“zigZag”的第一位。与3Electrologos答案的不同之处在于,您使用了一个测试。一个测试~禁用操作的管道化,比没有测试和分支的计算要慢。@chmike Yes这个答案是为了说明发生了什么,而不是为了性能,正如第一行中所承认的那样。谢谢。这个版本可以直观地看出为什么他认为编码方案有效。
int zigzag_to_signed(unsigned int zigzag)
{
    int abs = (int) (zigzag >> 1);

    if (zigzag % 2)
        return ~abs;
    else
        return abs;
}

unsigned int signed_to_zigzag(int signed)
{
    unsigned int abs = (unsigned int) signed << 1;

    if (signed < 0)
        return ~abs;
    else
        return abs;
}
( value >> 1 ) ^ ( ~( value & 1 ) + 1 )