在谷歌协议缓冲区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位整数
答案 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]