Skip to content
ZigZag Encoding for Signed Ints
step 1/5

Reading — step 1 of 5

Read

~1 min readSchema & Encoding

ZigZag Encoding for Signed Ints

Naively varint-encoding a signed integer is a disaster. Because protobuf sign-extends signed values to 64 bits before varint-encoding, the value -1 becomes 0xFFFFFFFFFFFFFFFF — a 10-byte varint. Every small negative number takes the maximum possible space.

ZigZag fixes this by mapping signed integers to unsigned ones so that values with small magnitude — positive or negative — get small encodings:

signedzigzag
00
-11
12
-23
24
......

The formula (for 32-bit):

zigzag(n) = (n << 1) ^ (n >> 31)

The arithmetic right shift turns negatives into -1 (all 1s) and positives into 0. XOR with n << 1 then flips all bits for negatives.

Decoding:

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

When to use it

Use sint32 / sint64 when your field commonly holds negative values (deltas, signed offsets, latitudes encoded as integers, etc.). For unsigned or always-positive values, stick with int32 / int64 / uint32 / uint64.

Wire compatibility caveat

int32, uint32, and sint32 all use wire type 0 (VARINT) — but their interpretations differ. Switching int32 -> sint32 in a schema is a breaking change even though the wire type stays the same: old data encoded as int32 will decode as wrong values when re-read as sint32.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…