Data Types & Conversion
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
| Type | Holds | Example |
|---|---|---|
int | Whole numbers (64 bits on 64-bit systems) | 42, -7 |
int8, int16, int32, int64 | Whole numbers of a fixed size | int8 holds -128 to 127 |
uint, uint8 ... uint64 | Whole numbers that cannot be negative | uint8 holds 0 to 255 |
float64, float32 | Numbers with a fractional part | 3.14, 1299.5 |
bool | true or false | true |
string | Text (a sequence of bytes, usually UTF-8) | "Pune" |
byte | Another name for uint8, one byte | 'a' as a byte |
rune | Another 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:
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)
}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:
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)
}-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:
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")
}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).
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
}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:
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)
}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 / 2to be2.5. With two integers the answer is2. Convert tofloat64first. - Using
string(n)to make "65" from 65. Usestrconv.Itoa(n). - Storing money in
float64. Rounding errors add up across thousands of bills; use paise in anint. - 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, sizedint/uinttypes), floats,bool,string,byteandrune. - 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.Itoaandstrconv.ParseFloatto 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
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")
}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?
A number with a decimal point defaults to float64.
2. After f := 7.9, what does int(f) evaluate to?
Converting a float variable to an int drops the fraction towards zero, so 7.9 becomes 7.
3. What happens when a uint8 holding 255 is increased by one at run time?
Integer overflow wraps around silently; 255 is the largest uint8.
4. How should you convert the number 2026 to the string "2026"?
strconv.Itoa formats an integer as decimal text; string(n) treats n as a Unicode code point.
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
