Go Module 4 – Arrays, slices, maps and strings
Numbers as text: strconv, bases and grouping
Parse integers, floats and booleans in Go with strconv and read *strconv.NumError, format numbers in any base, and group digits as 1,234,567 or 12,34,567.
What you will learn
- Parse integers, floats and booleans with strconv, and handle *strconv.NumError with errors.Is and errors.As
- Format integers in bases 2 to 36 and floats with a chosen precision
- Write integer literals with base prefixes and digit separators, and parse them with base 0
- Group the digits of an amount in thousands or in the Indian system, from integer minor units
- Validate amounts that people type, and return errors that name the problem
Before you start
What you’ll learn · Before you start
What you will learn
- Parse integers, floats and booleans with strconv, and handle *strconv.NumError with errors.Is and errors.As
- Format integers in bases 2 to 36 and floats with a chosen precision
- Write integer literals with base prefixes and digit separators, and parse them with base 0
- Group the digits of an amount in thousands or in the Indian system, from integer minor units
- Validate amounts that people type, and return errors that name the problem
Before you start
On this page
strconv.Atoi("42x") returns 0 and an error that explains itself: strconv.Atoi: parsing "42x": invalid syntax. Text
from forms, files and command lines becomes numbers through the strconv package, and numbers become text through it
again, in any base and with any precision.
You will parse integers, floats and booleans and read their errors, format numbers in other bases, and add the digit grouping that neither fmt nor strconv provides. The programs on this page ran with Go 1.27.1, each shown with the output it printed.
Parse functions return a value and an error that says why
Each parse function takes text and returns a value and an error. This program calls four of them on good and bad input:
// parse.go: strconv's parse functions, their results and the *strconv.NumError they return.
package main
import (
"errors"
"fmt"
"strconv"
)
func main() {
n, err := strconv.Atoi("42")
fmt.Println(n, err)
n, err = strconv.Atoi("42x")
fmt.Println(n, err)
big, err := strconv.ParseInt("ff", 16, 64) // base 16, result must fit 64 bits
fmt.Println(big, err)
small, err := strconv.ParseInt("300", 10, 8) // must fit 8 bits: -128 to 127
fmt.Println(small, err, errors.Is(err, strconv.ErrRange))
f, err := strconv.ParseFloat("3.25", 64)
fmt.Println(f, err)
b, err := strconv.ParseBool("yes")
fmt.Println(b, err)
var ne *strconv.NumError
if errors.As(err, &ne) {
fmt.Printf("Func %s, Num %q, Err %v\n", ne.Func, ne.Num, ne.Err)
}
} Output
42 <nil> 0 strconv.Atoi: parsing "42x": invalid syntax 255 <nil> 127 strconv.ParseInt: parsing "300": value out of range true 3.25 <nil> false strconv.ParseBool: parsing "yes": invalid syntax Func ParseBool, Num "yes", Err invalid syntax
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run parse.go
Atoi is ParseInt with base 10 and the size of int. ParseInt takes the base, 2 to 36, and a bit size, so
ParseInt("ff", 16, 64) read 255. Every error is a *strconv.NumError: The error type of strconv's parse functions. Its fields name the function, the input and the reason: strconv.ErrSyntax for text that is not a number, strconv.ErrRange for a number that does not fit., whose fields name the
function, the input and the reason: ErrSyntax for text that is not a number, ErrRange for a number that does not
fit. The last lines took the fields out with
A function of package errors that looks through an error, and every error it wraps, for one of a given type, and stores the first it finds in the variable its second argument points to.. Step through the two failing calls:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- Atoi("42x"). Atoi is ParseInt with base 10 and the size of int. It reads "42x" and meets x, which is not a base-10 digit, so the syntax check fails.
- A syntax error. The call returns 0 and a *strconv.NumError whose Err is ErrSyntax. Its message names the function and the input, strconv.Atoi parsing "42x", and the reason, invalid syntax.
- ParseInt("300", 10, 8). The second call's text is all digits, so the syntax check passes and the value is computed in base 10, 300.
- Too big for 8 bits. A bitSize of 8 means the result must fit an int8, from -128 to 127. The value 300 does not fit.
- A clamped value and ErrRange. ParseInt returns 127, the largest int8, with ErrRange in the error, as its documentation says. Code that keeps the value and ignores the error stores 127.
The second failure is the one to remember: for "300" with 8 bits, ParseInt returned 127 as well as the error, the
largest value that fits, as its documentation says.
A function of package errors that reports whether an error is a given error value or wraps one, so it finds strconv.ErrRange inside a *strconv.NumError. answered true for strconv.ErrRange, so code can tell the two kinds of failure apart.
Using the value without checking the error
n, _ := strconv.Atoi(field) turns every bad field into 0, and a number out of range into the limit of int
nearest to it, with no sign that anything went wrong. Check err first, as for any function that returns one.
ParseBool accepts only a fixed list, 1, t, T, TRUE, true and True and their false forms, so "yes" is invalid
syntax.
Key idea
Use a parsed value only when its error is nil: a failure returns 0 for bad text and the nearest limit for a number that does not fit, and errors.Is tells the two apart.
Checkpoint
Check: Parsing with errors
Format functions write numbers in any base and with any precision
In the other direction, strconv.FormatInt(n, base) writes an integer in a base: The number of different digits a way of writing numbers uses: 10 for decimal, 2 for binary, 16 for hexadecimal. strconv's FormatInt and ParseInt take it as an argument, from 2 to 36. from 2
to 36, and strconv.FormatFloat writes a float in a chosen format and precision:
// format.go: integers in other bases, floats with a chosen precision, and literals with prefixes and separators.
package main
import (
"fmt"
"strconv"
)
func main() {
fmt.Println(strconv.FormatInt(255, 2), strconv.FormatInt(255, 16), strconv.FormatInt(255, 36))
fmt.Println(strconv.Itoa(-7), strconv.Quote("tab\there"))
x := 1234.5678
fmt.Println(strconv.FormatFloat(x, 'f', 2, 64), strconv.FormatFloat(x, 'e', 3, 64), strconv.FormatFloat(x, 'g', -1, 64))
fmt.Printf("[%8.2f] [%-8.1f] [%010.3f]\n", x, x, x) // width and precision with fmt
buf := []byte("id=")
buf = strconv.AppendInt(buf, 4096, 10) // appends to a byte slice, no new string
fmt.Println(string(buf))
fmt.Println(0b1010, 0o17, 0x1F, 1_000_000) // literals: binary, octal, hex, digit separators
} Output
11111111 ff 73 -7 "tab\there" 1234.57 1.235e+03 1234.5678 [ 1234.57] [1234.6 ] [001234.568] id=4096 10 15 31 1000000
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run format.go
| Call | Result | What decides it |
|---|---|---|
FormatInt(255, 2), (255, 16), (255, 36) |
11111111, ff, 73 | the base; digits past 9 are letters |
FormatFloat(x, 'f', 2, 64) |
1234.57 | 2 digits after the point, rounded |
FormatFloat(x, 'e', 3, 64) |
1.235e+03 | 3 digits after the point, with an exponent |
FormatFloat(x, 'g', -1, 64) |
1234.5678 | -1: the fewest digits that read back exactly |
fmt.Printf("%010.3f", x) |
001234.568 | width 10, 3 decimals, padded with zeros |
strconv.Quote wrote the string as a Go literal, with \t for the tab, which makes invisible characters visible in
logs and error messages. The last argument of FormatFloat, 64, says the value came from a float64, and the
documentation explains that it rounds the result as that type. AppendInt wrote 4096 onto an existing byte slice,
which saves a string when output is built in a buffer. The last line printed literals written in Go’s own notation,
0b1010, 0o17, 0x1F and 1_000_000: the specification allows an underscore between digits for readability, and
it does not change the value.
Checkpoint
Check: Formatting in bases and precisions
Digit grouping is not built in, and it differs by place
Neither fmt nor strconv separates thousands: 1234567 prints as 1234567. digit grouping: Separators between groups of digits that make large numbers readable: by threes, 1,234,567, in most places, or the last three digits and then pairs, 12,34,567, in the Indian system. is a matter of local convention, and the Unicode standard for number formats describes two sizes, a
primary one for the last digits and a secondary one for the rest. Most places group by threes, 1,234,567; the Indian
system groups the last three digits and then pairs, 12,34,567. A few lines produce the first style:
// group.go: fmt and strconv print no digit grouping, so a few lines add commas every three digits.
package main
import (
"fmt"
"strconv"
)
// groupThousands puts a comma between every three digits of n, from the right. n must not be negative.
func groupThousands(n int64) string {
digits := strconv.FormatInt(n, 10)
out := make([]byte, 0, len(digits)+len(digits)/3)
for i := range len(digits) {
if i > 0 && (len(digits)-i)%3 == 0 {
out = append(out, ',')
}
out = append(out, digits[i])
}
return string(out)
}
func main() {
fmt.Println(1234567, fmt.Sprintf("%d", 1234567)) // no separators from fmt
for _, n := range []int64{0, 999, 1000, 1234567, 1000000000} {
fmt.Println(groupThousands(n))
}
} Output
1234567 1234567 0 999 1,000 1,234,567 1,000,000,000
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run group.go
Step through the loop for 1234567:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The digits first. strconv.FormatInt(1234567, 10) gives the digits as text, "1234567". The loop copies them one at a time and decides before each whether a comma comes first.
- No comma before the first. Before the first digit no comma is ever written, whatever the count; 7 digits are still to write.
- Six left. Before the digit 2, six digits are left to write, including this one. Six is a multiple of three, so a comma goes in, giving 1,2.
- Three left. The digits 3 and 4 leave 5 and 4 to write, so no comma. Before the 5, three digits are left, so another comma goes in, giving 1,234,5.
- Done. The last two digits need no comma, and the result is 1,234,567, as group.go printed for the same number.
The function counts the digits still to write and puts a comma before a digit whenever that count is a multiple of three, which gave 1,000 and 1,000,000,000 as well. It accepts only numbers of 0 or more, which a real formatter must not assume.
Checkpoint
Check: Digit grouping
Three tools complete the job. A grouping function for both styles handles negative numbers and the smallest int64. Money is printed from whole paise or cents with division and remainder. An amount parser reads grouped text back and says what is wrong with bad input. Outside the standard library, golang.org/x/text/message prints numbers with the separators of a chosen language.
Grouping for every int64, and money from integer units Pro: opens with a Pro, Premium or Ultimate pass
A grouping function looks finished after one test with a positive number, and a money formatter after one test with a round amount. This part reproduces the two bugs a first version usually has, builds a formatter for both styles, and prints money from integer units, next to the float64 shortcut that loses a paisa.
In this part:
- Two grouping bugs reproduced, and a function for both styles
- Money printed from integer units, never through float64
2 runnable examples (Go) · about 4 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).
Parsing what people type: amounts, prefixes and floats, with the failures shown Pro: opens with a Pro, Premium or Ultimate pass
Input typed by people arrives with commas, symbols, signs, leading zeros and the wrong number of decimals. This part builds an amount parser that refuses each mistake with a message that names it, then shows two parse settings that surprise: base 0 and floats.
In this part:
- An amount parser that names what is wrong
- Base 0 reads Go’s prefixes, and a leading 0 means octal
- Floats out of range, NaN, and rounding to two places
3 runnable examples (Go) · about 5 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise
Practice works through parsing with errors, formatting in bases and precisions, and digit grouping, starting with the idea your earlier answers left least certain. Each option says why it holds or where it slips:
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): What does strconv.Atoi return for “42x”?
It returns 0 and an error, a *strconv.NumError whose Func is “Atoi”, whose Num is “42x” and whose Err is
strconv.ErrSyntax. Printed, it reads strconv.Atoi: parsing "42x": invalid syntax. The caller must check the
error before using the value, since 0 is also a valid result.
Three longer questions of the module bank build on this lesson:
-
Fresher to mid
A CSV import reads quantities with strconv.ParseInt(field, 10, 8) to save memory, logs any error and stores the returned value. A row with 300 is stored as 127. Explain what happened and fix the import.
-
Mid
An API answers bad query parameters with the error text from strconv, such as strconv.ParseInt: parsing "12a": invalid syntax. What is wrong with that, and how do you turn a *strconv.NumError into good messages?
-
Fresher to mid
Write FormatAmount(paise int64) string, which prints amounts with Indian digit grouping, such as 12,34,567.89. Which edge cases must its tests cover, and why does each one exist?
Key takeaways
- strconv’s parse functions return a value and a
*strconv.NumError, withErrSyntaxfor text that is not a number andErrRange, together with the nearest limit of the type, for a number that does not fit. FormatIntwrites integers in any base from 2 to 36,FormatFloatwrites floats in a chosen format and precision, and precision -1 gives the fewest digits that read back exactly.- Go literals take the prefixes 0b, 0o and 0x and underscores between digits, which do not change the value.
- Neither fmt nor strconv groups digits, and grouping differs by place: by threes, 1,234,567, or in the Indian system, 12,34,567.
Terms in this lesson
- *strconv.NumError
- The error type of strconv's parse functions. Its fields name the function, the input and the reason: strconv.ErrSyntax for text that is not a number, strconv.ErrRange for a number that does not fit.
- base
- The number of different digits a way of writing numbers uses: 10 for decimal, 2 for binary, 16 for hexadecimal. strconv's FormatInt and ParseInt take it as an argument, from 2 to 36.
- digit grouping
- Separators between groups of digits that make large numbers readable: by threes, 1,234,567, in most places, or the last three digits and then pairs, 12,34,567, in the Indian system.
References
- Package strconv (The Go Authors)
- Package strconv: ParseInt (The Go Authors)
- Package strconv: FormatFloat (The Go Authors)
- The Go Programming Language Specification: Integer literals (The Go Authors)
- Package fmt (The Go Authors)
- Package errors: Is and As (The Go Authors)
- Unicode Technical Standard #35, Part 3: Numbers, Special Pattern Characters (Unicode Consortium)
- Package golang.org/x/text/message (The Go Authors)
- The Go Programming Language Specification: Arithmetic operators (The Go Authors)
- Input Validation Cheat Sheet (OWASP Foundation)
- Go Fuzzing (The Go Authors)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress