views:

299

answers:

4
+4  Q: 

Zig Zag Decoding

In the google protocol buffers encoding overview, they introduce something called "Zig Zag Encoding", this takes signed numbers, which have a small magnitude, and creates a series of unsigned numbers which have a small magnitude.

For example

Encoded => Plain
0 => 0
1 => -1
2 => 1
3 => -2
4 => 2
5 => -3
6 => 3

And so on. The encoding function they give for this is rather clever, it's:

(n << 1) ^ (n >> 31) //for a 32 bit integer

I understand how this works, however, I cannot for the life of me figure out how to reverse this and decode it back into signed 32 bit integers

A: 

I have found a solution, unfortunately it's not the one line beauty I was hoping for:

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

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

I'm sure there's some super-efficient bitwise operations that do this faster, but the function is straightforward. Here's a python implementation:

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]
aem
Thanks, but unfortunately this is in a network encoding system for a game, and this particular decode function is used many times per packet, many times per second - it's gotta be _fast_
Martin
+2  A: 

How about

(n>>1) - (n&1)*n
ergosys
well it works, but how?
Martin
n even: n/2 n odd: n/2 - n
ergosys
huh, that's pretty neat
Martin
Bit twiddling ftw!
LiraNuna
+4  A: 

Try this one:

(n >> 1) ^ (-(n & 1))

Edit:

I'm posting some sample code for verification:

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

I get following results:

0 => 0
1 => -1
2 => 1
3 => -2
4 => 2
5 => -3
6 => 3
7 => -4
8 => 4
9 => -5
3lectrologos
I knew there had to be a way around the multiply. Kudos!
ergosys
This one doesn't seem to work :/
Martin
Well, it works for me and for ergosys, so it should work for you too... Can you tell me what results you get?
3lectrologos
It may well be that I'm using it incorrectly. I have a UInt32 for n, and I'm casting the returned result into Int32 afterwards. Which sounds like a logical way to do things...
Martin
To be clear, the one Ergosys suggested works
Martin
The problem is that you use an unsigned int. Try using an Int32.
3lectrologos
The point of the function is that it converts an unsigned int into a signed int. If I simply cast my input into a signed int, I will lose a whole section of the upper range of my inputs
Martin
Actually, I don't think the unsigned int is the problem. I'm posting some code that works for me.
3lectrologos
Thanks very much :)
Martin
@3lectrologos, finally got to vote you up, I couldn't until you edited because I undid it once.
ergosys
I think this may be a language problem, translating that pretty much directly into C# results in an error, negating a UInt32 results in a long, and xor of long and UInt32 is undefined. I'm going to have a go at fixing it for C#
Martin
@ergosys: Thanks! :D
3lectrologos
return ((int)(u >> 1)) ^ ((int)(-(u The power of casting has fixed it. So, the question remains which should I use, from what ergosys said above I would assume this is faster due to a lack of multiplication?
Martin
@Martin: Can't you just cast it to a Int32 before negating it?
3lectrologos
It's much faster actually! :)
3lectrologos
My casts are killing your method. I just moved the cast to inside the negation as you suggested a couple of comments up, This is now the fastest method by a whisker
Martin
Yes, that should be as fast as it gets.
3lectrologos