Go Module 2 – Basics: values, types and control flow
Basic types: numbers, strings, bools, conversion
Go's number types and their ranges, explicit conversions and strconv, strings as bytes and plain bools, then overflow, division and money in whole cents.
What you will learn
- Choose between int, int64, uint8, float64 and the other number types
- Convert between number types, and between numbers and strings, with conversions and strconv
- Predict integer overflow, division and remainder results
- Represent money in whole cents without floating-point errors
Before you start
What you’ll learn · Before you start
What you will learn
- Choose between int, int64, uint8, float64 and the other number types
- Convert between number types, and between numbers and strings, with conversions and strconv
- Predict integer overflow, division and remainder results
- Represent money in whole cents without floating-point errors
Before you start
On this page
Go has fifteen number types, and it never converts between them on its own: adding an int to an int64 does not
compile, even where both are 64 bits wide. The strictness is deliberate, because the type of a number decides how big
it can get, whether it keeps a fraction, and what happens when a result does not fit.
You will choose a type for each kind of number, convert between types and to and from text, and see why the length of a string counts bytes and why no number can stand in for a bool.
Each number type has a fixed size and range
Integers come in four sizes, 8, 16, 32 and 64 bits, each signed, int8 to int64, or unsigned, uint8 to uint64.
int and uint are 32 or 64 bits wide depending on the platform, and 64 on 64-bit machines. byte is another name for
uint8, and rune, which holds one Unicode character, another name for int32. Fractions go in float32 or
float64. This program prints the ranges on a 64-bit Mac:
// types.go: the sizes and ranges of Go's number types, and two aliases.
package main
import (
"fmt"
"math"
"strconv"
)
func main() {
fmt.Println("int has", strconv.IntSize, "bits here, up to", math.MaxInt)
fmt.Println("int8: ", math.MinInt8, "to", math.MaxInt8)
fmt.Println("uint8:", 0, "to", math.MaxUint8)
fmt.Println("int32:", math.MinInt32, "to", math.MaxInt32)
fmt.Println("int64:", math.MinInt64, "to", math.MaxInt64)
fmt.Println("uint64: 0 to", uint64(math.MaxUint64)) // too big for int, so it is converted
var b byte = 'A'
var r rune = '€'
fmt.Printf("byte is %T, 'A' is %d; rune is %T, '€' is %d\n", b, b, r, r)
third := 1.0 / 3
fmt.Println("one third:", float32(third), "as float32,", third, "as float64")
} Output
int has 64 bits here, up to 9223372036854775807 int8: -128 to 127 uint8: 0 to 255 int32: -2147483648 to 2147483647 int64: -9223372036854775808 to 9223372036854775807 uint64: 0 to 18446744073709551615 byte is uint8, 'A' is 65; rune is int32, '€' is 8364 one third: 0.33333334 as float32, 0.3333333333333333 as float64
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run types.go
%T names the aliases by their real types, uint8 and int32, and ‘€’ is code point 8364. The last line shows the
precision: a float32 prints one third as 0.33333334, a float64 as 0.3333333333333333. Choose a type by what the
number holds. Answer the two questions for one number of your own:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Type | Counts and positions | Same size everywhere | Fractions | Exact money in cents |
|---|---|---|---|---|
| int Holds: whole numbers, 64 bits on 64-bit platforms | Good what len returns and an index takes | Mixed 32 bits on 32-bit platforms, 64 on 64-bit ones | Poor whole numbers only: 72 / 5 is 14 | Mixed exact, but its size depends on the platform |
| int64 Holds: whole numbers, 64 bits on every platform | Mixed needs int(i) to index a slice | Good 64 bits on every platform | Poor whole numbers only | Good exact whole cents, far beyond any bill |
| int32, uint16 … Holds: whole numbers of a smaller, fixed width | Mixed needs a conversion to index a slice | Good matches a field of a file format or protocol | Poor whole numbers only | Poor int32 cents stop at 21,474,836.47 |
| byte (uint8) Holds: 0 to 255 | Poor 255 is its largest value | Good 8 bits everywhere | Poor whole numbers only | Poor holds at most 255 cents |
| rune (int32) Holds: one Unicode code point | Poor meant for characters | Good 32 bits everywhere | Poor whole numbers only | Poor meant for characters, not amounts |
| float64 Holds: fractions, about 15 significant digits | Poor cannot index a slice | Good 64 bits everywhere | Good keeps fractions, and very large or small values | Poor 0.10 has no exact float64 value |
How the answers decide
- “What does the number hold?” A count, a length or a position: rules out byte (uint8), rune (int32) and float64 and adds a caveat to int64 and int32, uint16 …. Counts and positions are what len returns and an index takes, an int; any other integer type needs a conversion at every index.
- “What does the number hold?” A field of a file format or protocol: rules out float64 and rune (int32) and adds a caveat to int. A field of a format has a width the format fixes, so name that width, such as int32, uint16 or uint8; int changes size by platform.
- “What does the number hold?” A character of text: rules out int, int64, int32, uint16 … and float64 and adds a caveat to byte (uint8). A character is a rune, such as '€', which is 8364; a byte holds a whole character only in plain ASCII text.
- “What does the number hold?” A measurement with a fraction: rules out int, int64, int32, uint16 …, byte (uint8) and rune (int32). Only float64 keeps the fraction of a measurement; the integer types drop it.
- “Is it an amount of money?” Yes: rules out float64, byte (uint8), rune (int32) and int32, uint16 … and adds a caveat to int. Money needs exact whole cents. float64 cannot hold 0.10 exactly, and the small integer types run out of room.
In short: int for counts and positions, a sized type where a file format fixes the width, rune for characters and
float64 for measurements. Money goes in an int64 as whole cents, the smallest unit of the currency, because
float64 cannot hold 0.10 exactly. A result past a type’s range does not stop the program: it wraps around, so a
uint8 at 255 plus 1 is 0. At run time nothing warns when that happens, so choose a type wide enough for the largest
value the program will ever hold.
Checkpoint
Check: Number types, sizes and ranges
Go converts only where you write a conversion
A conversion: An expression such as int64(seats) that makes a value of the named type from another value. Go converts between types only where the code writes a conversion; untyped constants are the one exception. is the name of a type used like a function: int64(seats) makes an int64
from an int. Go never adds one for you, so even int and int64 cannot meet in one expression:
// mismatch.go: an int and an int64 in one expression.
package main
import "fmt"
func main() {
var seats int = 72
var berths int64 = 8
fmt.Println(seats + berths)
} Output (exit status 1)
Printed as an error (standard error)
# command-line-arguments ./mismatch.go:9:14: invalid operation: seats + berths (mismatched types int and int64)
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run mismatch.go
The fix is a conversion on one side. This program converts between number types and between numbers and text:
// conversions.go: Go converts between types only where you write it.
package main
import (
"fmt"
"strconv"
)
func main() {
var seats int = 72
var berths int64 = 8
total := int64(seats) + berths // int and int64 do not mix without a conversion
fmt.Println("total:", total)
perCoach := float64(seats) / 5 // 72 / 5 with integers would be 14
fmt.Println("per coach:", perCoach, "as int:", int(perCoach))
debt := -2.9
fmt.Println("int(-2.9):", int(debt)) // the fraction is dropped, toward zero
code := 65
fmt.Println(string(rune(code)), strconv.Itoa(code)) // the character 65, and 65 as text
n, err := strconv.Atoi("72")
fmt.Println(n+1, err)
_, err = strconv.Atoi("seventy")
fmt.Println(err)
} Output
total: 80 per coach: 14.4 as int: 14 int(-2.9): -2 A 65 73 <nil> strconv.Atoi: parsing "seventy": invalid syntax
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run conversions.go
A float converts to an integer by dropping its fraction, toward zero: int(14.4) is 14 and int(-2.9) is -2. An
untyped constant: A constant with no type yet, such as 5, 2.5 or "hi". It takes the type its use needs, as long as its value fits that type, and has a default type, such as int or float64, where nothing else gives it one. More in “Constants, untyped values and iota” such as 5 has no type of its own, so it takes the type of the other
operand without a conversion. Step through that division:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- An int. seats is an int holding 72. An int never meets a float64 in one expression without a conversion, not even in a division.
- The conversion. float64(seats) is a conversion. It makes a new float64 value, 72, and leaves seats itself an int.
- An untyped constant. 5 is a constant with no type yet. When the other operand of an operator has a type, Go converts an untyped constant to it on its own.
- Both sides match. So 5 becomes the float64 value 5, and both sides of the division have the same type, which every arithmetic operator needs.
- A float64 result. Dividing two float64 values gives 14.4. int(perCoach) then converts back to int by dropping the fraction, which gives 14.
- Without the conversion. seats / 5 divides two ints, because 5 takes seats' type, int. Integer division drops the remainder, so the result is 14 and the .4 is gone before any conversion could keep it.
Between numbers and text, use the package strconv: strconv.Itoa(65) gives “65”, and strconv.Atoi reads text back,
with an error for text that is not a whole number. string(65) gives “A”, the character with code 65, which is
almost never what was meant. go vet reports it:
// stringint.go: string(n) compiles, but it does not give the digits.
package main
import "fmt"
func main() {
seat := 65
fmt.Println("seat " + string(seat))
} from session import run
# string(n) compiles and runs, and go vet explains what it really does.
run(
"""
go run stringint.go
go vet stringint.go
""",
expect_failure=["go vet stringint.go"],
) Output
$ go run stringint.go seat A $ go vet stringint.go stringint.go:8:24: conversion from int to string yields a string of one rune, not a string of digits [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 vet_session.py
Checkpoint
Check: Explicit conversions and strconv
A string holds bytes, and a bool holds only true or false
A Go string is a sequence of bytes that never changes once it exists. A string literal holds its text as
UTF-8: The encoding of Go source code and string literals: each Unicode character takes one to four bytes, so 'A' takes one byte and '₹' three. len of a string counts these bytes., where ‘€’ takes three bytes, and len counts bytes:
// strings_bools.go: a string is a sequence of bytes; a bool is true or false.
package main
import (
"fmt"
"strconv"
)
func main() {
price := "€1999"
fmt.Println(len(price), "bytes") // € takes 3 bytes in UTF-8
fmt.Println(price[0], price[3], string(price[3]))
fmt.Println(price + " with tax") // + makes a new string
paid := len(price) > 0 // a comparison gives a bool
fmt.Println(paid, !paid, paid && false)
ok, err := strconv.ParseBool("yes")
fmt.Println(ok, err)
} Output
7 bytes 226 49 1 €1999 with tax true false false false strconv.ParseBool: parsing "yes": invalid syntax
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run strings_bools.go
“€1999” has length 7, not 5. Indexing gives bytes: price[0] is 226, the first byte of ‘€’, and price[3] is 49, the
digit 1. + builds a new string, and price[0] = 'R' does not compile, because a string’s bytes cannot be assigned.
A bool is true or false and nothing else. Comparisons produce one, and !, && and || combine them. No number
stands in for a bool, so if count {} with an int does not compile. strconv.ParseBool reads “true”, “false”, 1, 0
and a few short forms, and in the run it refused “yes”.
Checkpoint
Check: Strings as bytes, and bools
Overflow, division and remainder, reproduced Pro: opens with a Pro, Premium or Ultimate pass
Integer arithmetic in Go never stops a program because a result is too big, and it drops the fraction of every quotient toward zero. Here both rules run on real numbers, with the two ways a division by zero is caught.
In this part:
- What wraps at run time and what the compiler refuses
- Division drops the fraction toward zero
- A division by zero, caught at compile time or at run time
- Catching an overflow before it happens
1 diagram · 6 runnable examples (Go) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Money without float drift: cents, rounding and a fair split Pro: opens with a Pro, Premium or Ultimate pass
Floating-point numbers are the wrong home for money, and their errors are small enough to pass tests that use round numbers. This part measures the drift, keeps amounts in whole cents, writes the rounding down, and splits a bill so that the shares add up to the total.
In this part:
- Why 0.1 and 0.2 do not add up to 0.3, measured
- Amounts as whole cents, with the rounding written down
- Splitting 1,000.00 among three without losing a cent
1 diagram · 3 runnable examples (Go) · about 6 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise
This self-check covers number types, conversions, strings and bools. The session starts where you have been least sure, and a wrong choice tells you what it misses:
Practice that adapts to you
Practise this lesson
9 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): Is int the same type as int64 on a 64-bit machine?
No. They have the same size there, but they are different types, so an expression that mixes them does not compile
until one side is converted. The size of int belongs to the platform, 32 or 64 bits, while int64 is 64 bits
everywhere, so use int64 when the width matters.
Three longer questions of the module bank build on this lesson:
-
Fresher to mid
A daily report converts each order's float64 total to cents and adds them up. After 40,000 orders it is about 2,300 cents below the bank's statement, though every order prints correctly. Why does that happen, and how would you fix the code and the stored data?
-
Fresher to mid
A metric counts the bytes uploaded in an int32 field. After one large upload the dashboard shows a negative number. Explain what happened, how code could have caught it before it happened, and which type you would use instead.
-
Fresher to mid
A colleague builds cache keys with key := "order:" + string(order.ID). For the IDs 65 and 9731 the keys come out as "order:A" and "order:☃". What went wrong, how does Go's tooling catch it, and how do you convert numbers to text and back safely?
Key takeaways
- Every number type has a fixed size:
intis 64 bits on 64-bit platforms,byteisuint8andruneisint32. - Go converts between types only where you write a conversion, and a float converts to an integer by dropping the fraction toward zero.
- strconv converts between numbers and text;
string(n)gives a character, which go vet reports. - A string is a sequence of bytes that cannot change,
lencounts bytes, and a bool is onlytrueorfalse.
Terms in this lesson
- conversion
- An expression such as int64(seats) that makes a value of the named type from another value. Go converts between types only where the code writes a conversion; untyped constants are the one exception.
- UTF-8
- The encoding of Go source code and string literals: each Unicode character takes one to four bytes, so 'A' takes one byte and '₹' three. len of a string counts these bytes.
References
- The Go Programming Language Specification: Numeric types (The Go Authors)
- The Go Programming Language Specification: String types (The Go Authors)
- The Go Programming Language Specification: Conversions (The Go Authors)
- The Go Programming Language Specification: Arithmetic operators (The Go Authors)
- The Go Programming Language Specification: Integer overflow (The Go Authors)
- The Go Programming Language Specification: Constant expressions (The Go Authors)
- Frequently Asked Questions: Why does Go not provide implicit numeric conversions? (The Go Authors)
- Frequently Asked Questions: What is the size of an int on a 64 bit machine? (The Go Authors)
- Package strconv (The Go Authors)
- Package math/bits (The Go Authors)
- Package math: constants (The Go Authors)
- Command vet: stringintconv (The Go Authors)
- The Python Language Reference: Binary arithmetic operations (Python Software Foundation)
- IEEE Standard for Floating-Point Arithmetic (IEEE 754) (IEEE)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress