Zig Zag解码

时间:2010-02-05 22:32:12

标签: language-agnostic bit-manipulation protocol-buffers bitfoo zigzag-encoding

在谷歌协议缓冲区encoding overview中,它们引入了一种称为“Zig Zag编码”的东西,它采用了具有较小幅度的有符号数字,并创建了一系列具有较小幅度的无符号数字。

例如

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

我理解这是如何工作的,然而,我不能为我的生活弄清楚如何反转这个并将其解码回有符号的32位整数

6 个答案:

答案 0 :(得分:27)

试试这个:

(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;
}

我得到以下结果:

0 => 0
1 => -1
2 => 1
3 => -2
4 => 2
5 => -3
6 => 3
7 => -4
8 => 4
9 => -5

答案 1 :(得分:3)

怎么样

(n>>1) - (n&1)*n

答案 2 :(得分:3)

这是另一种做同样的方式,只是为了解释目的(显然你应该使用3lectrologos'单行)。

你只需要注意到xor的数字是全1(相当于按位不是)或全0(相当于什么都不做)。这就是(-(n & 1))产生的结果,或谷歌的“算术转移”评论所解释的内容。

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;
}

因此,为了在最重要的位置上有很多0,Zigzag编码使用LSB作为符号位,其他位作为绝对值(实际上仅用于正整数,负数用于绝对值-1)到2的补码表示。)

答案 3 :(得分:2)

在摆弄了3lectrologos提出的接受的答案后,我无法在使用unsigned longs(在C#中编译 - 错误)开始工作。我想出了类似的东西:

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

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

答案 4 :(得分:1)

我找到了一个解决方案,不幸的是,这并不是我所希望的一线美:

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

UInt32 a = (u >> 1) ^ signMask;
return *((Int32*)&a);

答案 5 :(得分:-1)

我确信有一些超级高效的按位操作可以更快地完成这项操作,但功能很简单。这是一个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]