Number types

Number typeProtocol Buffer typesMinimumMaximum
Signed 32-bit integerint32, sint32, sfixed32-231 = -2147483648231 - 1 = 2147483647
Signed 64-bit integerint64, sint64, sfixed64-263 = -9223372036854775808263 - 1 = 9223372036854775807
Unsigned 32-bit integeruint32, fixed320232 - 1 = 4294967295
Unsigned 64-bit integeruint64, fixed640264 - 1 = 18446744073709551615
32-bit floating pointfloat≈ -3.40 * 1038≈ 3.40 * 1038
64-bit floating pointdouble≈ -1.80 * 10308≈ 1.80 * 10308

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.

Which integer type to use

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.

Compatibility

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 < 231)
  • fixed32 → sfixed32 (if already stored values are < 231)
  • uint64 → int64 (if already stored values are < 263)
  • fixed64 → sfixed64 (if already stored values are < 263)

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.

Integer types storage requirements

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.

uint32fixed32
0 ≤ k < 2714
27 ≤ k < 21424
214 ≤ k < 22134
221 ≤ k < 22844
228 ≤ k < 23254
uint64fixed64
0 ≤ k < 2718
27 ≤ k < 21428
214 ≤ k < 22138
221 ≤ k < 22848
228 ≤ k < 23558
235 ≤ k < 24268
242 ≤ k < 24978
249 ≤ k < 25688
256 ≤ k < 26398
263 ≤ k < 264108
int32sint32sfixed32
-231 ≤ k < -2271054
-227 ≤ k < -2201044
-220 ≤ k < -2131034
-213 ≤ k < -261024
-26 ≤ k < 01014
0 ≤ k < 26114
26 ≤ k < 27124
27 ≤ k < 213224
213 ≤ k < 214234
214 ≤ k < 220334
220 ≤ k < 221344
221 ≤ k < 227444
227 ≤ k < 228454
228 ≤ k < 231554
int64sint64sfixed64
-263 ≤ k < -26210108
-262 ≤ k < -2551098
-255 ≤ k < -2481088
-248 ≤ k < -2411078
-241 ≤ k < -2341068
-234 ≤ k < -2271058
-227 ≤ k < -2201048
-220 ≤ k < -2131038
-213 ≤ k < -261028
-26 ≤ k < 01018
0 ≤ k < 26118
26 ≤ k < 27128
27 ≤ k < 213228
213 ≤ k < 214238
214 ≤ k < 220338
220 ≤ k < 221348
221 ≤ k < 227448
227 ≤ k < 228458
228 ≤ k < 234558
234 ≤ k < 235568
235 ≤ k < 241668
241 ≤ k < 242678
242 ≤ k < 248778
248 ≤ k < 249788
249 ≤ k < 255888
255 ≤ k < 256898
256 ≤ k < 262998
262 ≤ k < 2639108