Data Types & Conversion

Go · Lesson 7 of 32 6 min read Updated

Every value in Go has a type that decides what the value can hold and what you can do with it. A whole number of students, a price with paise, a yes-or-no flag and a customer's name all need different types. Go checks types when it compiles, so many mistakes never reach your users.

In this lesson you will meet Go's basic types, see what happens when numbers get too big, learn why floats are risky for money, and convert between types with conversions and the strconv package.

The basic types

TypeHoldsExample
intWhole numbers (64 bits on 64-bit systems)42, -7
int8, int16, int32, int64Whole numbers of a fixed sizeint8 holds -128 to 127
uint, uint8 ... uint64Whole numbers that cannot be negativeuint8 holds 0 to 255
float64, float32Numbers with a fractional part3.14, 1299.5
booltrue or falsetrue
stringText (a sequence of bytes, usually UTF-8)"Pune"
byteAnother name for uint8, one byte'a' as a byte
runeAnother name for int32, one Unicode character'₹'

Use int for ordinary whole numbers and float64 for decimals unless you have a reason to choose a sized type. Sized types matter for file formats, network protocols and saving memory in huge arrays.

When you write a value without a type, Go picks a default type. The %T verb of Printf shows a value's type:

Go
package main

import "fmt"

func main() {
	count := 42
	price := 99.5
	letter := 'A'
	city := "Kochi"
	open := true

	fmt.Printf("%T %T %T %T %T\n", count, price, letter, city, open)
}
Output
int float64 int32 string bool

A character in single quotes is a rune, which is really an int32 holding the character's Unicode number, so %T prints int32. Strings and runes get their own lesson: Strings, Bytes & Runes.

Overflow: when a number gets too big

Each integer type has a fixed size. If a calculation goes past the largest value, it wraps around to the other end without any error. This is called overflow:

Go
package main

import (
	"fmt"
	"math"
)

func main() {
	var level int8 = 127
	level++
	fmt.Println(level)

	var stock uint8 = 0
	stock--
	fmt.Println(stock)

	fmt.Println(math.MaxInt64)
}
Output
-128
255
9223372036854775807

127 is the largest int8, so adding one wraps to -128. A uint8 cannot be negative, so 0 minus one wraps to 255. The math package defines the limits of every type. A plain int goes up to about 9.2 quintillion, which is enough for almost everything, including money counted in paise.

The compiler does catch overflow in constants: var level int8 = 300 is a compile error because 300 can never fit.

Floats and money

float64 stores numbers in binary, and many decimal fractions, such as 0.1, have no exact binary form. Small errors appear:

Go
package main

import "fmt"

func main() {
	a := 0.1
	b := 0.2
	fmt.Println(a + b)
	fmt.Println(a+b == 0.3)

	// Money as whole paise avoids the problem
	pricePaise := 129950 // ₹1,299.50
	total := pricePaise * 3
	fmt.Println(total/100, "rupees and", total%100, "paise")
}
Output
0.30000000000000004
false
3898 rupees and 50 paise

For prices and bank balances, store whole paise in an int (or int64) and convert to rupees only for display. Use floats for measurements such as distance, temperature or averages, where a tiny error does not matter.

Converting between types

Go never converts numeric types for you. Adding an int to a float64 is a compile error. You convert explicitly by writing the type like a function: float64(x), int(y).

Go
package main

import "fmt"

func main() {
	totalMarks := 457
	subjects := 5

	fmt.Println(totalMarks / subjects) // integer division

	avg := float64(totalMarks) / float64(subjects)
	fmt.Println(avg)
	fmt.Println(int(avg)) // drops the fraction

	change := -2.7
	fmt.Println(int(change)) // towards zero, not down
}
Output
91
91.4
91
-2

Dividing two int values gives an int, and the fraction is thrown away. Convert first to get 91.4. Converting a float variable to an int cuts off the fractional part towards zero; it does not round. For rounding, use math.Round(avg). With a constant the rule is stricter: int(2.7) written directly is a compile error, because the constant 2.7 cannot be represented exactly as an int.

Text to numbers with strconv

A conversion like string(65) does not give "65". It gives the character with Unicode number 65, which is "A", and go vet warns about it. To turn numbers into text and back, use the strconv (string conversion) package:

Go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	qty, err := strconv.Atoi("12")
	fmt.Println(qty+1, err)

	_, err = strconv.Atoi("twelve")
	fmt.Println(err)

	year := strconv.Itoa(2026)
	fmt.Println(year + " batch")

	price, _ := strconv.ParseFloat("1299.50", 64)
	fmt.Println(price)
}
Output
13 <nil>
strconv.Atoi: parsing "twelve": invalid syntax
2026 batch
1299.5

Atoi (ASCII to integer) returns two values: the number and an error. When parsing succeeds the error is nil, printed as <nil>. When it fails you get a helpful message. Text from users, files and web forms can be anything, so always check that error; you will learn the full pattern in Errors: Wrapping, Is & As. Itoa goes the other way, and ParseFloat reads decimals (the 64 asks for float64 precision).

Common mistakes

  • Expecting 5 / 2 to be 2.5. With two integers the answer is 2. Convert to float64 first.
  • Using string(n) to make "65" from 65. Use strconv.Itoa(n).
  • Storing money in float64. Rounding errors add up across thousands of bills; use paise in an int.
  • Ignoring the error from Atoi. A failed parse returns 0, which can silently become a wrong order quantity.

Key points

  • Go's basic types are integers (int, sized int/uint types), floats, bool, string, byte and rune.
  • Integer overflow wraps around silently at run time; constant overflow is a compile error.
  • Floats cannot store most decimal fractions exactly, so keep money in whole paise.
  • Convert explicitly with T(value); float to int conversion cuts towards zero.
  • Use strconv.Atoi, strconv.Itoa and strconv.ParseFloat to move between text and numbers, and check the error.

Exercise

A tea stall in Jaipur sold 37 cups at ₹12.50 each. Store the price in paise as an int, calculate the total, and print it as rupees and paise. Then parse the text "8" (cups sold to a late customer) with strconv.Atoi, add it to the count, and print the new total in the same way.

Show solution
Go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	pricePaise := 1250
	cups := 37
	total := pricePaise * cups
	fmt.Println("Total:", total/100, "rupees", total%100, "paise")

	extra, err := strconv.Atoi("8")
	if err != nil {
		fmt.Println("bad number:", err)
		return
	}
	cups += extra
	total = pricePaise * cups
	fmt.Println("New total:", total/100, "rupees", total%100, "paise")
}
Output
Total: 462 rupees 50 paise
New total: 562 rupees 50 paise

37 cups make 46,250 paise, which is ₹462.50. With 45 cups the total is 56,250 paise. Keeping everything in int paise means no rounding errors.

Quick Quiz

1. What type does Go give to x := 3.0?

2. After f := 7.9, what does int(f) evaluate to?

3. What happens when a uint8 holding 255 is increased by one at run time?

4. How should you convert the number 2026 to the string "2026"?

Learn Go with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call