Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

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.

  • Intermediate
  • 15 minutes
  • Examples run with Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: Parse functions return a value and an error that says why · Format functions write numbers in any base and with any precision · Digit grouping is not built in, and it differs by place. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

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: Atoi, ParseInt, ParseFloat and ParseBool Go · parse.go
// 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:

Interactive · stage by stage How ParseInt decides, for "42x" and for "300" Step through two calls and watch where each one stops and what it returns.
How ParseInt decides, for "42x" and for "300", step 5 of 5. Showing Check the syntax ("300": all digits), Compute the value (300), Check the bitSize (8 bits: -128 to 127), Return value and error (127, value out of range). In focus: Return value and error.invalid syntaxCheck the syntax"300": all digitsCompute the value300Check the bitSize8 bits: -128 to 127Return value and error127, value out of range

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    n, err := strconv.Atoi("12a")
    fmt.Println(n, err)
    Show the answer: Question 1 of 2

    Answer: 0 strconv.Atoi: parsing "12a": invalid syntax

    parse.go printed the same for "42x": 0 and a *strconv.NumError with ErrSyntax.

  2. Question 2 of 2 What does strconv.ParseInt("-200", 10, 8) return?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: -128 and an error with ErrRange

    For "300" with 8 bits, parse.go printed 127 with value out of range; the documentation gives the maximum magnitude of the appropriate bit size and sign.

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: bases, float formats, padding, AppendInt and literals Go · format.go
// 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.

Number Base Converter Convert a number between binary, octal, decimal and hexadecimal to check a FormatInt result.

Checkpoint

Check: Formatting in bases and precisions

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    fmt.Println(strconv.FormatInt(10, 2), strconv.FormatInt(31, 16))
    Show the answer: Question 1 of 2

    Answer: 1010 1f

    format.go wrote 255 as 11111111, ff and 73 in bases 2, 16 and 36.

  2. Question 2 of 2 What does the precision -1 mean in strconv.FormatFloat(x, 'g', -1, 64)?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Use the fewest digits that ParseFloat reads back as exactly x

    The shortest form that reads back is what 'g' with -1 prints, and it is the safe way to write a float as text.

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: commas every three digits Go · group.go
// 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:

Interactive · stage by stage groupThousands on 1234567 Step through the loop and watch where it writes a comma, counting the digits still to write.
groupThousands on 1234567, step 5 of 5. Showing Digits "1234567", Digit 1: 7 left, Digit 2: 6 left, Digit 5: 3 left, The result. In focus: The result.Digits "1234567"from strconv.FormatIntDigit 1: 7 leftnever a comma firstDigit 2: 6 left6 is a multiple of 3Digit 5: 3 left3 is a multiple of 3The result1,234,567

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. 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.
  2. No comma before the first. Before the first digit no comma is ever written, whatever the count; 7 digits are still to write.
  3. 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.
  4. 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.
  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

  1. Question 1 of 2 Where does groupThousands put the first comma in 1234567890, a number of 10 digits?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: After the 1, because nine digits are left to write and nine is a multiple of three

    A comma goes before each digit that leaves a multiple of three digits to write, as the figure showed for 1234567.

  2. Question 2 of 2 What does fmt.Printf("%d\n", 1234567) print?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 1234567, with no separators

    Grouping is a local convention that the standard library leaves to your code or to golang.org/x/text/message.

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.

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.

  1. Question 1 of 9 Which fields does a *strconv.NumError have?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: Func, Num and Err

    parse.go printed Func ParseBool, Num "yes", Err invalid syntax.

  2. Question 2 of 9 Which inputs does strconv.ParseBool accept without an error?

    Choose every answer that is right.

    Show the answer: Question 2 of 9

    Answer:

    • "True"
    • "F"
    • "1"

    ParseBool accepts 1, t, T, TRUE, true, True and 0, f, F, FALSE, false, False, and nothing else.

  3. Question 3 of 9 An import must report "not a number" and "too large" differently. How does the code tell them apart?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: errors.Is(err, strconv.ErrSyntax) and errors.Is(err, strconv.ErrRange)

    parse.go printed errors.Is(err, strconv.ErrRange) as true for "300" with 8 bits.

  4. Question 4 of 9 Which bases does strconv.FormatInt accept?

    Choose one answer.

    Show the answer: Question 4 of 9

    Answer: 2 to 36

    FormatInt(255, 36) printed 73, the largest base the function accepts.

  5. Question 5 of 9 What does this print?

    Read the code, then choose one answer.

    fmt.Println(strconv.FormatFloat(3.14159, 'f', 2, 64))
    Show the answer: Question 5 of 9

    Answer: 3.14

    FormatFloat(x, 'f', 2, 64) gave 1234.57 for 1234.5678 in format.go.

  6. Question 6 of 9 What is the value of 0x1F + 0b11?

    Type a number.

    Show the answer: Question 6 of 9

    Answer: 34

    0x1F is 31 in hexadecimal and 0b11 is 3 in binary, so the sum is 34.

  7. Question 7 of 9 How does the Indian system group 1234567?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: 12,34,567

    The Unicode standard for number formats calls this a primary grouping of 3 and a secondary grouping of 2.

  8. Question 8 of 9 How many commas does groupThousands put into 1000000000?

    Type a number.

    Show the answer: Question 8 of 9

    Answer: 3 commas

    Ten digits get commas where 9, 6 and 3 digits are left: 1,000,000,000, as group.go printed.

  9. Question 9 of 9 A report must print amounts grouped by threes for one office and in the Indian style for another. What do you write?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: One formatter that takes the grouping style as an argument

    Grouping differs by place, so the place, or its style, belongs among the formatter's inputs.

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:

  1. 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.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. 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?

    Model answer, Pro. First step and follow-ups

  3. 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?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • strconv’s parse functions return a value and a *strconv.NumError, with ErrSyntax for text that is not a number and ErrRange, together with the nearest limit of the type, for a number that does not fit.
  • FormatInt writes integers in any base from 2 to 36, FormatFloat writes 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

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.