The fmt package has three families of print functions, and each family has the same three variants:
| Function | Output goes to | Format |
|---|---|---|
Print, Println, Printf | standard output | default, default with spaces and newline, format string |
Sprint, Sprintln, Sprintf | a returned string | same three styles |
Fprint, Fprintln, Fprintf | any io.Writer (file, buffer, HTTP response) | same three styles |
Errorf | a returned error | format string, plus %w for wrapping |
Println adds spaces between operands and a newline at the end. Printf adds nothing: you write \n yourself. Print adds spaces only between operands that are both non-strings, which is surprising enough that most code uses Println or Printf.
Format Verbs
General
| Verb | Prints | Example output |
|---|---|---|
%v | the value in a default format | {Ana 31 [admin]} |
%+v | structs with field names | {Name:Ana Age:31 Tags:[admin]} |
%#v | Go syntax for the value | main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}} |
%T | the type | main.User |
%% | a literal percent sign | % |
%+v is the one to reach for when debugging. A pointer to a struct prints as &{...} rather than an address. Maps print with keys sorted, so output is stable even though map iteration order is random.
Integers
| Verb | Meaning | fmt.Sprintf(verb, 255) |
|---|---|---|
%d | decimal | 255 |
%b | binary | 11111111 |
%o | octal | 377 |
%O | octal with 0o prefix | 0o377 |
%x / %X | hex, lower or upper case | ff / FF |
%#x | hex with 0x prefix | 0xff |
%c | the character with that code point | ÿ |
%q | a quoted character literal | 'ÿ' |
%U | Unicode format | U+00FF |
Floats
| Verb | Meaning | fmt.Sprintf(verb, 1234.5678) |
|---|---|---|
%f | decimal, 6 places by default | 1234.567800 |
%.2f | decimal, 2 places | 1234.57 |
%e | scientific notation | 1.234568e+03 |
%g | %e or %f, whichever is shorter, no trailing zeros | 1234.5678 |
%v | same as %g | 1234.5678 |
%.2f rounds the float's exact binary value, so fmt.Sprintf("%.2f", 2.675) is 2.67: the nearest float64 to 2.675 is slightly below it. Never format money from a float; keep cents in an integer.
Strings and bytes
| Verb | Meaning | fmt.Sprintf(verb, "go\n") |
|---|---|---|
%s | the plain string | go and a newline |
%q | double-quoted, escapes shown | "go\n" |
%x | hex of each byte | 676f0a |
% x | hex with spaces | 67 6f 0a |
%s on a []byte prints it as text; %v prints the numbers ([104 105]).
Other types
| Verb | Type | Prints |
|---|---|---|
%t | bool | true or false |
%p | pointer, slice, map, channel, func | the address, like 0xc000012345 |
%w | error (only in Errorf) | the error's message, and wraps it |
Width, Precision and Padding
Between % and the verb you can put flags, a width and a precision:
| Form | Effect |
|---|---|
%5d | width 5, right-aligned (pad on the left with spaces) |
%-5d | width 5, left-aligned |
%05d | pad with zeros |
%.2f | 2 digits after the decimal point |
%8.2f | width 8 and 2 decimals |
%.3s | at most 3 characters of the string |
%+d | always show the sign |
%*d | width taken from the next argument |
Note %.0f of 2.5 prints 2: Go rounds half to even here. Width counts runes for strings, not display columns, so CJK characters and emoji can still misalign a table. For aligned columns of variable text, text/tabwriter does the measuring for you.
Argument indexes
%[n] picks an argument by position, which lets you reuse one:
fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10) // 10 a 1010
Errorf and %w
fmt.Errorf builds an error from a format string. With %w it also wraps another error, so callers can still detect the original:
Use %w when callers may need to check the cause, and %v when you deliberately hide it. Since Go 1.20 one Errorf call may contain several %w verbs. The error handling page covers wrapping in depth.
Custom Formatting with String()
Any type with a String() string method controls how %v, %s and Println show it:
%d bypasses String() and prints the underlying number. For error types the equivalent method is Error() string, which takes precedence over String().
When the Verb Is Wrong
fmt never panics on a bad format. It prints the problem inline:
fmt.Printf("%d\n", "oops")
fmt.Printf("%d %d\n", 1)
fmt.Printf("%d\n", 1, 2)
%!d(string=oops)
1 %!d(MISSING)
1
%!(EXTRA int=2)
That output tends to reach production because it does not crash anything. go vet catches all three at build time:
./main.go:8:2: fmt.Printf format %d has arg "oops" of wrong type string
./main.go:9:2: fmt.Printf format %d reads arg #2, but call has 1 arg
./main.go:10:2: fmt.Printf call needs 1 arg but has 2 args
Performance Notes
fmt takes every argument as an any and inspects its type at run time (falling back to reflection for structs, slices and maps), which is fine for logging and output but measurable in tight loops. For converting a single number, strconv.Itoa and strconv.FormatFloat are faster than Sprintf. For building a long string in a loop, write into a strings.Builder with fmt.Fprintf(&b, ...) instead of concatenating Sprintf results.
Frequently Asked Questions
What is the difference between Println, Printf and Sprintf in Go?
fmt.Println prints its arguments separated by spaces with a newline at the end. fmt.Printf prints according to a format string and adds no newline. fmt.Sprintf formats the same way as Printf but returns the result as a string instead of printing it. fmt.Errorf does the same and returns an error.
How do I print a struct with field names in Go?
Use %+v: fmt.Printf("%+v\n", user) prints {Name:Ana Age:31}. %v prints only the values, {Ana 31}, and %#v prints Go syntax including the type, main.User{Name:"Ana", Age:31}.
How do I format a float to 2 decimal places in Go?
Use %.2f: fmt.Sprintf("%.2f", 3.14159) returns "3.14". Add a width to align columns, %8.2f, or a minus sign to left-align, %-8.2f. strconv.FormatFloat(f, 'f', 2, 64) gives the same result without a format string.
What does %w do in fmt.Errorf?
%w formats an error like %v and also wraps it, so the new error carries the original. errors.Is and errors.As can then find the wrapped error: err := fmt.Errorf("load config: %w", os.ErrNotExist) makes errors.Is(err, os.ErrNotExist) true. %w only works in fmt.Errorf.
Why does my output show %!d(string=...)?
The verb does not match the argument's type, for example %d given a string. fmt prints the problem inline instead of panicking: %!d(string=oops). Missing arguments print %!d(MISSING) and extra ones %!(EXTRA int=2). go vet catches all three before you run the program.