Number type | Protocol Buffer types | Minimum | Maximum |
---|---|---|---|

Signed 32-bit integer | int32, sint32, sfixed32 | -2^{31} = -2147483648 | 2^{31} - 1 = 2147483647 |

Signed 64-bit integer | int64, sint64, sfixed64 | -2^{63} = -9223372036854775808 | 2^{63} - 1 = 9223372036854775807 |

Unsigned 32-bit integer | uint32, fixed32 | 0 | 2^{32} - 1 = 4294967295 |

Unsigned 64-bit integer | uint64, fixed64 | 0 | 2^{64} - 1 = 18446744073709551615 |

32-bit floating point | float | ≈ -3.40 * 10^{38} | ≈ 3.40 * 10^{38} |

64-bit floating point | double | ≈ -1.80 * 10^{308} | ≈ 1.80 * 10^{308} |

The floating point types can also have the special values ∞ (infinity), -∞ (negative infinity) and NaN (not a number).

See the Wikipedia articles for float and double
for more information or try this Float Toy to play around with floating point numbers.

Enums are encoded as an int32 field.

As you can see there is plenty of choice in integer data types. After deciding what range (unsigned/signed, 32-bit/64-bit) you need for your data type, there are still some options to choose from. The difference between these options, for example between int32, sint32 and sfixed32, is only how the data is encoded. It's about saving a few bytes. You're probably not transfering Protocol Buffer messages that are gigabytes in size, so it won't really matter. But in case you're interested, here are a few pointers:

- The sint32, sint64, uint32 and uint64 types take a variable amount of space. They are encoded in such a way that values closer to zero use less bytes. These should be your default choice.
- The fixed32, fixed64, sfixed32 and sfixed64 store the data as-is. That is, using 4 or 8 bytes. These data types are efficient when your application likely uses the high bits of the integer. A typical example is when you store a hash such as CRC32, fixed32 is perfect for that.
- The int32 and int64 types store positive values the same way as uint32 and uint64. Negative values always take 10 bytes to be stored, even for int32. You could use int32/int64 as alternative to uint32/uint64 if you know you won't need the highest bit anyway. But when you need to store negative values you'll be better off with an other data type in almost all cases.

For the exact amount of storage for each encoding, see the tables at the end of this page.

It may happen that your initial choice for a data type turns out to not be the best choice. Can you still use an other data type in your proto file without losing compatibility with existing serialized data? In some cases, you can.

The following protocol buffer data types are compatible with each other:

- int32, int64, uint32, uint64
- sint32, sint64
- fixed32, sfixed32
- fixed64, sfixed64

In this context, compatible means that values that are in the supported range of both data types are encoded the same way. A few cases that you may encounter in practice:

It can happen that you chose a 32-bit data type, but later it turned out that 32 bits were not enough. You can safely make the following changes in your proto file without losing backwards compatibility with existing data:

- int32 → int64
- uint32 → uint64
- sint32 → sint64

If you make such a change, you have to keep in mind that your old software (that uses the 32-bit data type) will not read values outside of the range of the 32-bit data type correctly. The Protocol Buffers standard specifies that all the higher bits must be dropped.

Similarly, if you have an unsigned data type in your proto file, but in a later version of your software it turns out you need negative values, then you could make the following replacements:

- uint32 → int64
- uint32 → int32 (if already stored values are < 2
^{31}) - fixed32 → sfixed32 (if already stored values are < 2
^{31}) - uint64 → int64 (if already stored values are < 2
^{63}) - fixed64 → sfixed64 (if already stored values are < 2
^{63})

Here the same remark: Your new software will be compatible with your old data, but your old software will not be compatible with new values that are outside of the range of your old data type.

An alternative is to just make a new field, and of course planning ahead can save you a lot of trouble.

These tables give the amount of bytes it takes to store a number using a certain encoding. This is only the number itself, not including the field tag.

uint32 | fixed32 | |
---|---|---|

0 ≤ k < 2^{7} | 1 | 4 |

2^{7} ≤ k < 2^{14} | 2 | 4 |

2^{14} ≤ k < 2^{21} | 3 | 4 |

2^{21} ≤ k < 2^{28} | 4 | 4 |

2^{28} ≤ k < 2^{32} | 5 | 4 |

uint64 | fixed64 | |
---|---|---|

0 ≤ k < 2^{7} | 1 | 8 |

2^{7} ≤ k < 2^{14} | 2 | 8 |

2^{14} ≤ k < 2^{21} | 3 | 8 |

2^{21} ≤ k < 2^{28} | 4 | 8 |

2^{28} ≤ k < 2^{35} | 5 | 8 |

2^{35} ≤ k < 2^{42} | 6 | 8 |

2^{42} ≤ k < 2^{49} | 7 | 8 |

2^{49} ≤ k < 2^{56} | 8 | 8 |

2^{56} ≤ k < 2^{63} | 9 | 8 |

2^{63} ≤ k < 2^{64} | 10 | 8 |

int32 | sint32 | sfixed32 | |
---|---|---|---|

-2^{31} ≤ k < -2^{27} | 10 | 5 | 4 |

-2^{27} ≤ k < -2^{20} | 10 | 4 | 4 |

-2^{20} ≤ k < -2^{13} | 10 | 3 | 4 |

-2^{13} ≤ k < -2^{6} | 10 | 2 | 4 |

-2^{6} ≤ k < 0 | 10 | 1 | 4 |

0 ≤ k < 2^{6} | 1 | 1 | 4 |

2^{6} ≤ k < 2^{7} | 1 | 2 | 4 |

2^{7} ≤ k < 2^{13} | 2 | 2 | 4 |

2^{13} ≤ k < 2^{14} | 2 | 3 | 4 |

2^{14} ≤ k < 2^{20} | 3 | 3 | 4 |

2^{20} ≤ k < 2^{21} | 3 | 4 | 4 |

2^{21} ≤ k < 2^{27} | 4 | 4 | 4 |

2^{27} ≤ k < 2^{28} | 4 | 5 | 4 |

2^{28} ≤ k < 2^{31} | 5 | 5 | 4 |

int64 | sint64 | sfixed64 | |
---|---|---|---|

-2^{63} ≤ k < -2^{62} | 10 | 10 | 8 |

-2^{62} ≤ k < -2^{55} | 10 | 9 | 8 |

-2^{55} ≤ k < -2^{48} | 10 | 8 | 8 |

-2^{48} ≤ k < -2^{41} | 10 | 7 | 8 |

-2^{41} ≤ k < -2^{34} | 10 | 6 | 8 |

-2^{34} ≤ k < -2^{27} | 10 | 5 | 8 |

-2^{27} ≤ k < -2^{20} | 10 | 4 | 8 |

-2^{20} ≤ k < -2^{13} | 10 | 3 | 8 |

-2^{13} ≤ k < -2^{6} | 10 | 2 | 8 |

-2^{6} ≤ k < 0 | 10 | 1 | 8 |

0 ≤ k < 2^{6} | 1 | 1 | 8 |

2^{6} ≤ k < 2^{7} | 1 | 2 | 8 |

2^{7} ≤ k < 2^{13} | 2 | 2 | 8 |

2^{13} ≤ k < 2^{14} | 2 | 3 | 8 |

2^{14} ≤ k < 2^{20} | 3 | 3 | 8 |

2^{20} ≤ k < 2^{21} | 3 | 4 | 8 |

2^{21} ≤ k < 2^{27} | 4 | 4 | 8 |

2^{27} ≤ k < 2^{28} | 4 | 5 | 8 |

2^{28} ≤ k < 2^{34} | 5 | 5 | 8 |

2^{34} ≤ k < 2^{35} | 5 | 6 | 8 |

2^{35} ≤ k < 2^{41} | 6 | 6 | 8 |

2^{41} ≤ k < 2^{42} | 6 | 7 | 8 |

2^{42} ≤ k < 2^{48} | 7 | 7 | 8 |

2^{48} ≤ k < 2^{49} | 7 | 8 | 8 |

2^{49} ≤ k < 2^{55} | 8 | 8 | 8 |

2^{55} ≤ k < 2^{56} | 8 | 9 | 8 |

2^{56} ≤ k < 2^{62} | 9 | 9 | 8 |

2^{62} ≤ k < 2^{63} | 9 | 10 | 8 |