Skip to main content

Type Conversions

Go is strictly typed and never converts types implicitly. Every type change must be written explicitly using the conversion syntax:

targetType(value)

Mixing incompatible types without a conversion is a compile-time error — not a runtime warning, not a silent coercion:

x := 42
y := x + 2.5 // invalid operation: x + 2.5 (mismatched types int and float64)

// Correct — explicit conversion required
y := float64(x) + 2.5

Numeric Conversions​

int → float64​

Widening to a larger type is always safe — no data is lost:

i := 42
f := float64(i)

fmt.Printf("i: %d (type: %T)\n", i, i) // i: 42 (type: int)
fmt.Printf("f: %f (type: %T)\n", f, f) // f: 42.000000 (type: float64)

float64 → int (truncation)​

float64 can hold fractional values; int cannot. When converting, Go truncates toward zero — the fractional part is dropped, not rounded:

f2 := 9.99
i2 := int(f2)
fmt.Printf("%f → %d\n", f2, i2) // 9.990000 → 9 (0.99 dropped, not rounded to 10)

f3 := -9.99
i3 := int(f3)
fmt.Printf("%f → %d\n", f3, i3) // -9.990000 → -9 (toward zero, not -10)

If you need rounding, use math.Round() before converting:

import "math"

rounded := int(math.Round(9.99)) // 10
Inputint() resultNote
9.19truncated
9.99truncated, not rounded
-9.9-9toward zero, not -10

Widening: int16 → int32​

Converting a smaller integer type to a larger one is always safe — the value is always preserved:

var n16 int16 = 1000
n32 := int32(n16)

fmt.Printf("n16: %d (type: %T)\n", n16, n16) // n16: 1000 (type: int16)
fmt.Printf("n32: %d (type: %T)\n", n32, n32) // n32: 1000 (type: int32)

Narrowing: int32 → int8 (silent overflow)​

Converting a larger type to a smaller one can overflow. Go does not panic or return an error — it simply truncates the bits:

var big int32 = 2147483647 // max int32
small := int8(big) // overflows silently

fmt.Printf("big: %d (type: %T)\n", big, big) // big: 2147483647 (type: int32)
fmt.Printf("small: %d (type: %T)\n", small, small) // small: -1 (type: int8)

It is your responsibility to ensure the value fits before narrowing.

Key Concepts — Numeric Conversions​

ConceptDescription
Explicit syntaxtargetType(value) — e.g. float64(x), int(f)
Truncationfloat64 → int drops the fraction, always toward zero
WideningSmall → large type: always safe, value preserved
NarrowingLarge → small type: can overflow silently, no panic
No implicit conversionEvery type change must be written explicitly

String Conversions​

The string(n) gotcha​

string(n) on an integer does not produce the number as text. It produces the UTF-8 character for that Unicode code point:

fmt.Println(string(65))   // "A"  — Unicode code point 65 is the letter A
fmt.Println(string(9786)) // "☺" — Unicode code point 9786 is a smiley face

To get the numeric digits as a string, use strconv.Itoa or fmt.Sprintf:

  • strconv.Itoa(n) — converts an integer to its decimal string representation. "Itoa" stands for "Integer to ASCII".
  • fmt.Sprintf("%d", n) — formats a value into a string using a format verb. %d means decimal integer.
fmt.Println(strconv.Itoa(65))      // "65"
fmt.Println(fmt.Sprintf("%d", 65)) // "65"

int → string​

n := 65

fmt.Println(strconv.Itoa(n)) // "65" — Itoa = "Integer to ASCII"
fmt.Println(fmt.Sprintf("%d", n)) // "65" — format verb %d for decimal integer

Both approaches produce identical output. strconv.Itoa is idiomatic when you only need the conversion; fmt.Sprintf is more flexible for building larger strings.

string → int​

strconv.Atoi returns two values: the parsed integer and an error. Always check the error before using the result:

s := "42"
n, err := strconv.Atoi(s)
if err != nil {
fmt.Println("parse error:", err)
} else {
fmt.Printf("parsed: %d (type: %T)\n", n, n) // parsed: 42 (type: int)
}

When the string is not a valid integer, Atoi returns 0 (the zero value) and a non-nil error:

bad, err := strconv.Atoi("hello")
// err: strconv.Atoi: parsing "hello": invalid syntax
// bad: 0 — zero value returned on error

float64 → string​

Use strconv.FormatFloat. The arguments are: value, format ('f' for decimal), precision (decimal places), and bit size (64 for float64):

f := 3.14159
fStr := strconv.FormatFloat(f, 'f', 2, 64)

fmt.Println(fStr) // "3.14"

string → float64​

strconv.ParseFloat also returns two values — the result and an error:

fParsed, err := strconv.ParseFloat("3.14159", 64)
if err != nil {
fmt.Println("parse error:", err)
} else {
fmt.Printf("parsed: %f (type: %T)\n", fParsed, fParsed)
// parsed: 3.141590 (type: float64)
}

Key Concepts — String Conversions​

FunctionDirectionNotes
strconv.Itoa(n)int → string"Integer to ASCII"
strconv.Atoi(s)string → intReturns (int, error); zero value on failure
strconv.FormatFloat(f, 'f', prec, 64)float64 → stringControl precision with the prec argument
strconv.ParseFloat(s, 64)string → float64Returns (float64, error)
fmt.Sprintf("%d", n)int → stringAlternative using format verbs
string(n)int → string⚠️ Produces Unicode character, not digits