A brief summary (TL; DR) is at the end of the answer.
Untyped constants of arbitrary precision do not live at run time, constants live only at compile time (at compile time). At the same time, Go does not have to represent constants with arbitrary precision at runtime, only when compiling the application.
Why? Because constants are not compiled into executable files. They should not be. Let's take your example:
const Huge = 1e1000 fmt.Println(Huge / 1e999)
There is a Huge constant in the source code (and it will be in the package object), but it will not appear in your executable file. Instead, the function call in fmt.Println() will be written with the value passed to it, whose type will be float64 . Thus, only the float64 value, which is 10.0 , will be written to the executable file. There is no indication that the executable contains 1e1000 .
This type of float64 is derived from the default type for the untyped Huge constant. 1e1000 is a floating point literal . To check this:
const Huge = 1e1000 x := Huge / 1e999 fmt.Printf("%T", x)
Back to arbitrary precision:
Spec: Constants:
Numeric constants are exact values ββof arbitrary precision and do not overflow.
Thus, constants are exact values ββof arbitrary accuracy. As we have seen, it is not necessary to represent constants with arbitrary precision at runtime, but the compiler must still do something at compile time. And so it is!
Obviously, it is impossible to cope with "infinite" accuracy. But this is not necessary, since the source code itself is not "infinite" (the size of the source is finite). However, it is not practical to allow truly arbitrary accuracy. Thus, the specification gives some freedom to compilers regarding this:
Implementation limitation: although numerical constants have arbitrary precision in the language, the compiler can implement them using an internal representation with limited precision. However, each implementation should:
- Represent integer constants of at least 256 bits.
- They represent floating point constants, including parts of a complex constant, with a mantissa of at least 256 bits and an exponent of at least 32 bits.
- Give an error if you cannot accurately represent an integer constant.
- Throws an error if it cannot represent a floating point or a complex constant due to overflow.
- Round to the nearest representable constant if you cannot represent a floating point or complex constant due to accuracy limitations. These requirements apply to both literal constants and the result of evaluating constant expressions .
However, also note that when all of the above says, the standard package provides you with tools for representing and working with values ββ(constants) with "arbitrary" precision, see the go/constant package. You can look at its source to understand how it is implemented.
Implementation in go/constant/value.go . Types representing such values:
As you can see, the math/big package is used to represent untyped values ββof arbitrary precision. big.Int , for example (from math/big/int.go ):
Where nat (from math/big/nat.go ):
And finally, Word (from math/big/arith.go )
Summary
At run time: predefined types provide limited precision, but you can "simulate" arbitrary precision with specific packages such as math/big and go/constant . At compile time: constants seem to provide arbitrary precision, but in reality the compiler may not match this (not required); but nevertheless, the specification provides minimal accuracy for the constants that the entire compiler must support, for example, integer constants must be represented by at least 256 bits, which is 32 bytes (compared to int64 , which is "only" 8 bytes).
When an executable binary file is created, the results of constant expressions (with arbitrary precision) must be converted and represented by type values ββwith finite precision - which may not be possible and, therefore, may lead to errors during compilation. Please note that only the results - not intermediate operands - must be converted to finite precision, constant operations are performed with arbitrary precision.
How this arbitrary or improved precision is implemented is not determined by the specification, for example, math/big stores the βdigitsβ of a number in a slice (where the digits are not digits of the 10th base representation, and the βdigitβ is uintptr , which is similar to the base representation 4294967295 in 32-bit architectures and even more on 64-bit architectures).