So we’ve gone through the high-level conversion stuff… now we dive into some particulars. Today, numeric conversions.
Conversions between numeric types
For the conversion of non-constant numeric values, the following rules apply:
- When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type’s size. For example, if
v := uint16(0x10F0)
, thenuint32(int8(v)) == 0xFFFFFFF0
. The conversion always yields a valid value; there is no indication of overflow.
In other words, the fact that the sign uses a single bit of a signed integer’s capacity won’t impact the precision during conversion. But you can still suffer data loss during such a conversion, if the target’s type has less precision (either in terms of bits, or sign).
- When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
Simple enough. No rounding happens… just truncation.
var π float64 = 3.14159
var pi = int(π) // 3
- When converting an integer or floating-point number to a floating-point type, or a complex number to another complex type, the result value is rounded to the precision specified by the destination type. For instance, the value of a variable
x
of typefloat32
may be stored using additional precision beyond that of an IEEE-754 32-bit number, butfloat32(x)
represents the result of roundingx
’s value to 32-bit precision. Similarly,x + 0.1
may use more than 32 bits of precision, butfloat32(x + 0.1)
does not.
So in this case rounding actually happens, not truncation. This means that when you convert between floating point or complex types, the absolute value of the result may be larger than the absolute value of the original.
var a float64 = 1.0000003
var b = float32(a) // 1.0000004
In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.
We saw a similar note last month when looking at floating point operators: An implementation may choose to panic in certain cases. I suspect both of these “implementation-dependent” rules exist to allow working on CPU architectures without different floating point support.
Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)