skip to content

Formatting and Parsing

Turning values into text with fmt and text into values with strconv, including the verbs that print type information and the typed errors a failed parse returns.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

explore

questions

18

In Go's fmt, how do the %f, %e and %g verbs differ when printing a float64?

level: juniorimportance: must knowfreq 62%

answer

  1. three ways to spell one number
  2. one of them never shows an exponent
  3. two of them share a default of six
  4. the third prints only what it must
  5. shortest digits that read back the same

basics

~20 s

%f prints decimal digits with no exponent, %e prints scientific notation, and %g picks between them by magnitude. %f and %e default to six digits after the point; %g defaults to the shortest digits that identify the value uniquely.

solid answer

~40 s

All three print a `float64`; they differ in shape and in default precision. `%f` is plain decimal with no exponent and defaults to six places after the point, so `1234.5678` prints as `1234.567800`. `%e` is always scientific, also six places by default: `1.234568e+03`. `%g` chooses exponent form for very large or very small magnitudes and plain decimal otherwise, and its default precision is not six — it is the smallest number of digits that identifies the value uniquely, which is exactly what `%v` and `fmt.Println` use for floats. An explicit precision means different things per verb: for `%f` and `%e` it counts digits after the decimal point, for `%g` it caps total significant digits and trailing zeros are dropped. Width is separate from precision: `%9.2f` means minimum field width nine, precision two.

code

go · 9 lines
go
v := 1234.5678
// 1234.567800 — %f defaults to 6 places after the point
fmt.Printf("%f\n", v)
// 1234.57 — explicit precision counts places after the point
fmt.Printf("%.2f\n", v)
// 1.234568e+03 — %e also defaults to 6 places
fmt.Printf("%e\n", v)
// 1234.5678 — %g (and %v) print the shortest unique digits
fmt.Printf("%g\n", v)

go deeper

for a junior

Be ready to say what each of %f, %e and %g prints for a concrete value, and that %f and %e default to six decimals while %g prints the shortest unique form. Knowing that fmt.Println uses %g for floats is the piece most candidates miss.

for a middle

Explain the mechanics: precision means decimal places for %f and %e but significant digits for %g, and %g's default precision is the shortest-unique rule rather than a fixed count. Be able to say when %g flips to exponent form.

for a senior

Show the production judgment: fixed precision is a lossy output format and shortest-unique is not, so pick the verb by whether a human or a parser reads the result. Expect to catch a log or CSV that prints 1e+06 where a downstream tool wanted digits.

for a principal

Own the convention: which formats your services emit for numeric fields, whether human-facing output and machine-facing output are allowed to share a code path, and how that choice is enforced in review rather than rediscovered in a parsing incident.

## The three float verbs Go's `fmt` package formats a `float64` through several verbs. The three you meet every day are `%f`, `%e` and `%g`. - **`%f`** — plain decimal notation, never an exponent: `1234.567800`, `0.000000`. - **`%e`** — scientific notation: one digit before the point, then a signed exponent, `1.234568e+03`. `%E` is identical but uppercases the `e`. - **`%g`** — chooses between the two: exponent form when the value's decimal exponent is very large or very small, plain decimal otherwise, and it never emits trailing zeros. `%G` uppercases the exponent. Underneath, all of them call into `strconv`, whose `FormatFloat` takes the same format bytes `'f'`, `'e'` and `'g'`. So anything true of the verb is true of the `strconv` call and vice versa. ## Default precision is the real difference With no explicit precision, `%f` and `%e` print **six digits after the decimal point** — the C heritage. `%g` has no such fixed default. Its default precision is *the smallest number of digits necessary to represent the value uniquely*: the shortest decimal string that, read back, yields the identical `float64`. This matters because **`%v` for a float is `%g`**, and `fmt.Println` uses `%v`. So: ``` a, b := 0.1, 0.2 fmt.Println(a + b) // 0.30000000000000004 fmt.Printf("%f\n", a+b) // 0.300000 ``` Go is not being pedantic in the first line. The sum of the two nearest `float64` values to 0.1 and 0.2 genuinely is not the nearest `float64` to 0.3, and the shortest-unique rule is what exposes it. `%f` with six places hides it. Neither is wrong; they answer different questions — "what is this value?" versus "how should this value look in a column?". (Write the same expression as a single untyped constant, `fmt.Println(0.1 + 0.2)`, and Go folds it exactly at compile time before it ever becomes a `float64`, so the surprise disappears. Use variables when you want to see runtime float behaviour.) ## What an explicit precision means, verb by verb | Written | Meaning for a float64 | |---|---| | `%.2f` | two digits after the decimal point | | `%.2e` | two digits after the point in the mantissa | | `%.2g` | at most two **significant** digits in total, trailing zeros removed | | `%9.2f` | minimum field width 9, precision 2 (right-aligned, space-padded) | | `%-9.2f` | same, left-aligned | | `%09.2f` | same, zero-padded | | `%+.2f` | always print a sign | Reading `%.3g` as "three digits after the point" is the classic misread; it is three significant digits, so `1234.5` becomes `1.23e+03`. Rounding is to nearest, computed from the exact binary value rather than from an intermediate decimal approximation, so `%.2f` of a value slightly under a half-cent will not round up merely because a shorter print looked like it should. ## When %g switches to exponent form At default precision, `%g` uses exponent form when the value's decimal exponent is below -4 or at least 6. Concretely: ``` fmt.Println(999999.0) // 999999 fmt.Println(1000000.0) // 1e+06 fmt.Println(0.0001) // 0.0001 fmt.Println(0.00001) // 1e-05 ``` This catches people who print quantities, counters or identifiers that happened to arrive as floats (from JSON, say) and are startled to see `1e+06` in a log or a CSV. If the output is consumed by something that expects plain digits, say so with `%.0f` rather than relying on `%v`. ## Choosing a verb - **Human-facing, fixed scale** — a report column, a price, a percentage: `%f` with an explicit precision, plus a width if the column must line up. - **Machine-readable, must be reconstructible** — a wire format, a fixture, a log another program parses: `%g`/`%v` at default precision, or `strconv.FormatFloat(x, 'g', -1, 64)`, which is guaranteed to read back as the same value. A fixed precision is *lossy*; a shortest-unique print is not. - **Wide dynamic range** — physical measurements spanning many orders of magnitude: `%e` or `%g`. There is also `%x`, which prints a hexadecimal floating-point form such as `0x1.34a456d5cfaadp+10`. It is exact and compact, and it is useful in tests and bug reports where you need to name a specific bit pattern without argument. ## Special values Under every one of these verbs, a not-a-number prints as `NaN` and the infinities print as `+Inf` and `-Inf`, and the precision is ignored: `%.2f` of positive infinity is `+Inf`, not `+Inf.00`. Width still applies, so `%8.2f` pads it. ## float32 `fmt` knows the argument's type, so a `float32` is printed with the shortest string that is unique *among float32 values*: `fmt.Println(float32(0.1))` prints `0.1`, while `fmt.Println(float64(float32(0.1)))` prints `0.10000000149011612`. Same bits, different question about which set of values must be distinguished.

  • Which verb does fmt use for a float64 printed with %v or fmt.Println?
    `%g` at default precision. That is why `fmt.Println` on a float shows the shortest decimal that uniquely identifies the value — `0.30000000000000004` rather than `0.300000` — and why it can flip to exponent form, printing `1e+06` for a million.
  • What does the precision in %.3g mean, and how does it differ from %.3f?
    `%.3g` caps the total number of significant digits at three and strips trailing zeros, so `1234.5` prints as `1.23e+03`. `%.3f` counts three digits *after the decimal point*, giving `1234.500`. Misreading `%g`'s precision as decimal places is the common error.
  • How do width and precision combine in a verb like %9.2f?
    Width is the minimum number of columns the field occupies; precision is the digit count. `%9.2f` pads with spaces to at least nine columns and prints two decimals. Add `-` to left-align, `0` to zero-pad, `+` to force the sign. Width never truncates — an oversized number simply overflows the field.

saying these in an interview costs you the question

  • Thinks %v on a float prints six decimals like %f
  • Says %g always uses scientific notation
  • Reads %.3g as three digits after the decimal point
  • Assumes fmt.Println of a float never shows an exponent
  • Believes %f truncates rather than rounds to nearest
  • Confuses the width in %9.2f with the digit count
open as a page

What does strconv.Atoi return when the input string is not a valid integer?

level: juniorimportance: must knowfreq 82%

basics

~20 s

strconv.Atoi returns two values, the parsed int and an error. On bad input it returns 0 plus a non-nil error rather than panicking, so the 0 is meaningless until you have checked that the error is nil.

open as a page

In Go, how do the fmt verbs %v, %+v and %#v differ when printing a struct?

level: juniorimportance: must knowfreq 78%

basics

~10 s

%v prints only a struct's field values, like {s-42 <nil>}. %+v adds the field names. %#v prints a Go-syntax literal with the package-qualified type name and quoted strings, like main.Session{ID:"s-42"}.

open as a page

Why does strconv.FormatFloat with precision -1 round-trip exactly through ParseFloat?

level: middleimportance: must knowfreq 48%

basics

~20 s

Precision -1 makes strconv.FormatFloat emit the fewest digits that no other float64 shares, so strconv.ParseFloat reads the string back as the identical value. Any fixed precision can print a string that parses back as a different number.

open as a page

When does fmt call a type's String() method, and why can a pointer receiver hide it?

level: middleimportance: must knowfreq 66%

basics

~20 s

fmt calls String() when the operand satisfies fmt.Stringer and the verb is %v, %s, %q, %x or %X. If String has a pointer receiver, only *T satisfies the interface, so printing a T value falls back to default formatting.

open as a page

Why does fmt.Sprintf with the %d verb cost more than strconv.Itoa for the same int?

level: juniorimportance: should knowfreq 58%

basics

~20 s

fmt.Sprintf boxes the int into an interface value, scans the format string at run time, and dispatches on the argument's type before writing digits. strconv.Itoa runs a digit loop directly, so it is several times faster and allocates less.

open as a page

Why does converting an int with string(n) in Go not produce its decimal digits?

level: juniorimportance: should knowfreq 66%

basics

~20 s

A conversion from an integer to string treats the number as a Unicode code point, so string(65) is the one-character string A, not 65. Use strconv.Itoa for the decimal text, or strconv.FormatInt for another base.

open as a page

What makes printing a large struct with fmt's %+v verb expensive at run time?

level: middleimportance: should knowfreq 38%

basics

~20 s

The %+v verb drops fmt out of its fast type switch into a reflective walk: every field is visited by name, nested structs, slices and maps are recursed into, map keys are sorted, and String methods found on the way are called.

open as a page

How do Go's float verbs and strconv.ParseFloat handle NaN and the infinities?

level: middleimportance: should knowfreq 38%

basics

~20 s

Go prints them as NaN, +Inf and -Inf under every float verb, ignoring precision. strconv.ParseFloat accepts NaN, Inf and Infinity case-insensitively, so the text round-trips — but NaN never equals itself, and encoding/json refuses to marshal either.

open as a page

In strconv.ParseInt, what do the base and bitSize arguments control?

level: middleimportance: should knowfreq 54%

basics

~20 s

base is the radix, 2 through 36, or 0 to infer it from a 0x, 0o, 0b or leading-zero prefix. bitSize says which signed width the value must fit: 0, 8, 16, 32 or 64. The result is always an int64.

open as a page

In a Go fmt verb such as %8.2f, what do the width and the precision each control?

level: middleimportance: should knowfreq 42%

basics

~10 s

Width is the minimum output size, padded with spaces, so %8.2f never prints fewer than 8 characters. Precision follows the dot and is verb-dependent: decimal places for %f, a truncation limit for %s.

open as a page

A metrics daemon builds every StatsD line with fmt.Sprintf and GC CPU climbs with traffic. How do you cut the allocations per line?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Stop producing strings. Keep one reusable byte slice per emitter, reset it with buf = buf[:0], build the line with append and strconv.AppendInt, and write those bytes straight out. That removes the boxing, the format scan and two string copies.

open as a page

A Go ETL job exports float64 readings with %.3f and re-ingests them; totals drift. How do you diagnose it?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Fixed-precision formatting is lossy: each re-ingested row can differ by half a unit in the last printed place, and the errors accumulate. Prove it with a round-trip test, then emit shortest-unique text or round once at the source.

open as a page

A String() method on *Node returns fmt.Sprintf("Node(%v)", n) for its own receiver. What happens at runtime?

level: seniorimportance: should knowfreq 36%

basics

~20 s

It recurses forever: %v sees that *Node implements fmt.Stringer and calls String again. The goroutine's stack grows until it exceeds the runtime limit, and the process dies with fatal error: stack overflow, which recover cannot stop.

open as a page

How do you decide whether a Go service carries money as int64 minor units or float64?

level: principalimportance: should knowfreq 30%

basics

~20 s

Default to int64 minor units for anything summed, compared or reconciled, and treat the exported scale as a contract with the finance-data and API owners rather than a formatting detail. Reserve float64 for measurements nobody reconciles to the cent.

open as a page

Why can strconv.ParseInt return a huge value together with a non-nil error?

level: seniorimportance: nice to knowfreq 31%

basics

~20 s

Because an out-of-range input still returns a number: strconv.ParseInt hands back the largest value of the requested width and sign along with a *strconv.NumError whose Err field is strconv.ErrRange. Malformed text instead returns zero with ErrSyntax.

open as a page

If a type implements both error and fmt.Stringer, which method does fmt.Printf("%v", x) call?

level: seniorimportance: nice to knowfreq 28%

basics

~10 s

Error() wins. For the string-accepting verbs fmt checks error before fmt.Stringer, so the String method is dead code on a type that is also an error. Only fmt.Formatter takes precedence over both.

open as a page

When do you require a team to replace fmt.Sprintf with strconv.AppendInt-style calls, and how do you stop that spreading?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Only where a measurement ties formatting allocations to a cost you care about, and only inside a boundary narrow enough to write in one sentence. Everywhere else fmt.Sprintf stays, because readability is the default and the rewrite has to be maintained.

open as a page