Lexical elements: Integer literals

January 16, 2023

Integer literals

Ah integer literals! Now we’re starting to get into the meat of the language.

Go lets us express integers in four bases. 10 (decimal) is by far the most common, followed by base 16 (hexidecimal). Base 8 (octal) and base 2 (binary) are also supported, but seen fairly rarely.

Here’s how the spec describes it:

An integer literal is a sequence of digits representing an integer constant. An optional prefix sets a non-decimal base: 0b or 0B for binary, 0, 0o, or 0O for octal, and 0x or 0X for hexadecimal. A single 0 is considered a decimal zero. In hexadecimal literals, letters a through f and A through F represent values 10 through 15.

The astute reader may wonder why octal supports three prefixes, 0, 0o and 0O, whereas binary and hexidecimal each support only two (their upper- and lowercase- versions).

This is for historical reasons. Prior to the release of Go 1.13 in September 2019, there were no binary literals, and octal literals were indicated solely by the 0 prefix.

To make everything more consistent in Go 1.13, they added binary, with the respective 0b and 0B prefixes, and added the 0o and 0O prefixes for octal literals, to be consistent with the 0x and 0X prefixes already supported for hexidecimal literals.

These days, the plain 0 prefix should be considered obsolete, although it will continue to work indefinitely, if you’re using the standard gofmt tool (and you should be!), it will reformat any octal literals with the old bare 0 prefix to use 0o.

For readability, an underscore character _ may appear after a base prefix or between successive digits; such underscores do not change the literal’s value.

At the same time that Binary literals and standard Ocatl prefixes were added, they added the option to include arbitrary undescores (_) to literals. This should be used when it enhanches readability, such as to group by thousands when writing decimal literals.

int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit    = "0" | ( "1"  "9" ) [ [ "_" ] decimal_digits ] .
binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .

decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits  = binary_digit { [ "_" ] binary_digit } .
octal_digits   = octal_digit { [ "_" ] octal_digit } .
hex_digits     = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600       // second character is capital letter 'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727

_42         // an identifier, not an integer literal
42_         // invalid: _ must separate successive digits
4__2        // invalid: only one _ at a time
0_xBadFace  // invalid: _ must separate successive digits

Quotes from The Go Programming Language Specification, Version of June 29, 2022


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Architecture-independent Numeric Types

Go provides several predeclared numeric types. Most of them have a fixed size, regardless of the architecture the program is compiled for or running on. Numeric Types An integer, floating-point, or complex type represents the set of integer, floating-point, or complex values, respectively. They are collectively called numeric types. The predeclared architecture-independent numeric types are: uint8 the set of all unsigned 8-bit integers (0 to 255) uint16 the set of all unsigned 16-bit integers (0 to 65535) uint32 the set of all unsigned 32-bit integers (0 to 4294967295) uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615) int8 the set of all signed 8-bit integers (-128 to 127) int16 the set of all signed 16-bit integers (-32768 to 32767) int32 the set of all signed 32-bit integers (-2147483648 to 2147483647) int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807) float32 the set of all IEEE-754 32-bit floating-point numbers float64 the set of all IEEE-754 64-bit floating-point numbers complex64 the set of all complex numbers with float32 real and imaginary parts complex128 the set of all complex numbers with float64 real and imaginary parts byte alias for uint8 rune alias for int32 The value of an n-bit integer is n bits wide and represented using two’s complement arithmetic.


Integer context keys

We’re looking at different types for context keys. So far, we’ve looked at empty structs (struct{}), and found it to be less than ideal. Today, let’s consider integer context keys. This seems handy, right? type contextKey int const ( KeyUserID int = iota KeyTransactionID KeyMessageID . . . KeyFoo ) Let’s see how it stacks up to our 5 criteria: Must be comparable. ✅ No problem! Use minimal membory. ✅ int doesn’t have as small a memory footprint as struct{}’s zero bytes, but it’s still pretty small.


IncDec statements

IncDec statements The “++” and “–” statements increment or decrement their operands by the untyped constant 1. As with an assignment, the operand must be addressable or a map index expression. IncDecStmt = Expression ( "++" | "--" ) . Most commonly you’ll see ++ and -- used on a simple variable. But as explained, it’s not strictly limited to that. These operators can be used on any addressable (numeric) expression.

Get daily content like this in your inbox!

Subscribe