Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
printf Width and Precision
Master printf width and precision in C: align columns, control decimal places and trim strings with %5d, %7.2f and %.3s. A hands-on lesson from our free embedded C programming course, with runnable programs and real output.
Printing a value is easy. Printing it so a human can read a column of values at a glance is a skill. Sensor logs, UART debug dumps and register tables all rely on one feature of the format string: printf width and precision in C. Width says how much room a value gets; precision says how many decimals, digits or characters it may use.
In this lecture of our free embedded C programming course you will learn how %wd, %w.nf and %w.ns behave, why a value is never cut off by its width, why %.0f turns 2.5 into 2, and how to print neat tables. These are everyday tools in our free embedded systems course, free embedded Linux course, free Linux device drivers course and free Linux kernel development course, where you will print debug data constantly.
What You Will Learn
- The general layout of a printf conversion: flags, width, precision and conversion letter
- How width right-justifies integers and why a too-small width never truncates
- How precision rounds floats and what the default of six decimals means
- How precision trims strings and how width pads them
- How to left-justify, zero-pad and take width from a variable with the star
- How to avoid classic mistakes such as float rounding surprises and format string bugs
Prerequisites
- The earlier lectures in this chapter on printf, conversion specifiers and escape sequences
- Knowledge of
int,float,doubleand strings - GCC installed (programs here were built with GCC 13 on Ubuntu 24.04 using
-std=c11)
What Are printf Width and Precision in C?
A printf conversion has this general shape:
%[flags][width][.precision]conversion
Everything in square brackets is optional. The width is a whole number that sets the minimum number of characters the value occupies. The precision is a dot followed by a number, and its meaning depends on the type of the value.
| Value type | Width does this | Precision does this | Example |
|---|---|---|---|
| Integer (%d) | Pads with blanks on the left | Sets a minimum number of digits (rarely used) | %5d |
| Floating point (%f) | Pads the whole number including the point | Number of digits after the decimal point, rounded | %7.2f |
| String (%s) | Pads with blanks on the left | Maximum number of characters printed | %8.3s |
One rule covers all three
Width only ever adds blanks. It never removes anything. If the value is wider than the width, printf prints all of it and the column simply grows.
printf Width with %d: Right-Justified by Default
When the width is larger than the number of characters in the value, printf adds blanks on the left, so numbers line up on their last digit. That is exactly what you want for a column of numbers. The program below wraps each result in square brackets so the blanks become visible.
#include <stdio.h>
int main(void)
{
int a = 78, b = 9, c = 4096;
printf("[%3d]\n", a); /* shorter than width */
printf("[%4d]\n", b);
printf("[%4d]\n", c); /* exactly equal */
printf("[%2d]\n", c); /* longer: not cut */
printf("[%-6d]\n", a); /* left-justified */
printf("[%06d]\n", a); /* zero padded */
printf("[%+6d]\n", a); /* sign always shown */
printf("[%08x]\n", 0xBEEFu); /* zero-padded hex */
int n = printf("%8d", 42);
printf(" <- printf returned %d\n", n);
return 0;
}
gcc -std=c11 -Wall -Wextra -o p_int p_int.c
./p_int
[ 78]
[ 9]
[4096]
[4096]
[78 ]
[000078]
[ +78]
[0000beef]
42 <- printf returned 8
[%3d] with 78
[%4d] with 9
[%4d] with 4096
[%2d] with 4096
Legend
Read the first four lines against the diagram. The value 78 has two characters but the width is three, so one blank appears. The value 9 has one character and a width of four, so three blanks appear. The value 4096 exactly fills %4d. And with %2d the width is too small, yet all four digits are printed. This is the behaviour that protects you from silently losing data in your output.
Flags That Work with Width
| Flag | Effect | Example | Result |
|---|---|---|---|
- | Left-justify inside the width | [%-6d] with 78 | [78 ] |
0 | Pad with zeros instead of blanks | [%06d] with 78 | [000078] |
+ | Always show the sign | [%+6d] with 78 | [ +78] |
Zero padding is a favourite in embedded work: %08x prints a 32-bit register value with all eight hex digits, so 0xBEEF appears as 0000beef and columns of addresses stay aligned.
Using the Return Value to Measure Output
The last two lines of the program show something many students never notice: printf returns the number of characters it printed. With %8d the value 42 produced exactly 8 characters, six blanks and two digits. If you ever doubt how wide your output is, capture the return value and print it.
Try it yourself: predict the result
What does printf("[%5d]", -42) print? Remember that the minus sign is a character too. Answer: [ -42], two blanks, the sign and two digits.
printf Width and Precision for Floats: %w.nf
For floating-point values the width counts the entire printed number: digits before the point, the point itself, digits after it and the sign. The precision, after the dot, decides how many digits appear after the point. If you omit the precision, printf uses six digits, which is why printf("%f", 3.14) shows 3.140000.
#include <stdio.h>
int main(void)
{
float t = 8.0f, h = 5.9f;
printf("[%4.1f] [%7.2f]\n", t, h);
printf("[%4.1f] [%7.2f]\n", 25.3, 1635.92);
printf("[%4.1f] [%7.2f]\n", 15.231, 65.875948);
printf("[%f]\n", 3.14); /* default: 6 decimals */
printf("[%.0f] [%.0f] [%.0f] [%.0f]\n", 0.5, 1.5, 2.5, 3.5);
printf("[%.1f] [%.1f] [%.2f]\n", 0.25, 0.35, 2.675);
return 0;
}
gcc -std=c11 -Wall -Wextra -o p_float p_float.c
./p_float
[ 8.0] [ 5.90]
[25.3] [1635.92]
[15.2] [ 65.88]
[3.140000]
[0] [2] [2] [4]
[0.2] [0.3] [2.67]
Printed
Legend
Take the third line: [%4.1f] with 15.231 prints [15.2]. The width of four is exactly met by “15.2”, so no blanks. Then [%7.2f] with 65.875948 prints [ 65.88]. The precision of 2 rounded 65.875948 up to 65.88, and the remaining seven-character width added two blanks on the left.
Rounding Surprises You Should Know
The last two lines of the output look odd at first. %.0f turns 0.5 into 0, 1.5 into 2, 2.5 into 2 and 3.5 into 4. When a value sits exactly halfway, the C library on Linux rounds to the nearest even digit. And %.2f of 2.675 prints 2.67, not 2.68, because 2.675 cannot be stored exactly in binary; the stored value is a hair below 2.675. Neither is a bug. If you need decimal-style rounding for money, keep amounts in integers (for example paise or cents) and format them yourself.
float, double and the Right Conversion
In printf both float and double arguments use %f, because a float is promoted to double when passed through the variable argument list. A %lf is also accepted and means the same. This is different from scanf, where %f is for float and %lf is for double.
printf does not support %f unless you add the linker option -u _printf_float. If your floats print as empty, check this first. It costs extra flash, which is why many firmware projects print scaled integers instead.
printf Width and Precision for Strings: %w.ns
For strings, the precision is a cut-off: only the first n characters are printed. Then the width pads the result. So a conversion such as %8.3s first trims the text to three characters, then right-justifies those three characters in eight columns, giving five blanks.
#include <stdio.h>
int main(void)
{
const char *fw = "firmware";
printf("[%3s]\n", fw); /* width smaller: whole string */
printf("[%10s]\n", "uart"); /* right-justified */
printf("[%-10s]\n", "uart"); /* left-justified */
printf("[%.3s]\n", fw); /* precision: first 3 chars */
printf("[%8.3s]\n", fw); /* width and precision */
printf("[%-8.3s]\n", fw);
printf("[%.0s]\n", fw); /* precision 0: nothing */
return 0;
}
gcc -std=c11 -Wall -Wextra -o p_str p_str.c
./p_str
[firmware]
[ uart]
[uart ]
[fir]
[ fir]
[fir ]
[]
Printed
Legend
The number of blanks is always the width minus the number of characters actually printed. In this case that is 8 minus 3, which is 5. Notice also that %3s with an eight-letter word printed the whole word, because width never truncates, while %.3s did cut it. Width and precision are different tools, and mixing them up is the most common mistake with strings.
Dynamic Width with the Star: Building Aligned Tables
Sometimes the width is not known until the program runs. Replace the number with * and pass the value as an extra argument before the thing being printed. The same works for precision. This is a printf feature only; scanf uses the star for something else, as you will see in the next lecture.
#include <stdio.h>
struct reading {
const char *name;
int adc; /* 12-bit raw value, 0..4095 */
};
int main(void)
{
struct reading ch[] = {
{ "VBAT", 3012 },
{ "TEMP", 871 },
{ "LIGHT", 45 },
{ "MIC", 4095 },
};
printf("%-8s %6s %8s\n", "CHANNEL", "ADC", "VOLTS");
for (int i = 0; i < 4; i++)
printf("%-8s %6d %8.3f\n",
ch[i].name, ch[i].adc, ch[i].adc * 3.3 / 4095.0);
int w = 10, p = 4;
printf("[%*d]\n", w, 42); /* width from a variable */
printf("[%*.*f]\n", w, p, 3.14159); /* width and precision, both */
return 0;
}
gcc -std=c11 -Wall -Wextra -o p_table p_table.c
./p_table
CHANNEL ADC VOLTS
VBAT 3012 2.427
TEMP 871 0.702
LIGHT 45 0.036
MIC 4095 3.300
[ 42]
[ 3.1416]
The header and every row use the same widths (%-8s for names, %6d for raw counts and %8.3f for volts), so the columns stay aligned no matter what values appear. The voltage column is the 12-bit raw ADC value scaled against a 3.3 V reference. The last two lines show the star in action: %*d takes the width from w, and %*.*f takes width and then precision, in that order, before the value.
Real-World Uses in Embedded Programming
- Sensor dashboards on a serial terminal: fixed-width columns make trends visible while you tweak a control loop.
- Register and memory dumps:
%08xkeeps addresses and values aligned when you debug a peripheral. - Log timestamps:
%02d:%02d:%02dgives 07:05:09 instead of 7:5:9. - Safe string display:
%.16sshows at most 16 characters of a device name that might arrive without a terminator.
Common Mistakes and Troubleshooting
| Mistake | What you see | Fix |
|---|---|---|
| Expecting width to cut a long value | Column grows and misaligns | Use precision for strings, or make the width large enough |
Using %d to print a float | Garbage numbers (undefined behaviour) | Use %f and match every argument type to its conversion |
| Forgetting the point and sign in the float width | Alignment off by one or two | Count the sign and decimal point |
| Star arguments in the wrong order | Strange widths or a crash | Order is width, then precision, then the value |
| Floats print blank on a microcontroller | Empty field in the output | Enable float support in newlib-nano or print scaled integers |
| Trusting rounding of 2.675 | Result differs from hand calculation | Remember binary storage; use integers for money |
Best Practices, Performance and Security
- Use the same format string for headers and rows so columns match.
- Prefer
snprintfwith a buffer size oversprintfwhen building text. - Never pass untrusted text as the format: write
printf("%s", text), notprintf(text). A stray%nor%xin user data can leak or corrupt memory. This is called a format string vulnerability. - Performance: printf pulls in a lot of code and stack on small microcontrollers and formats slowly. For hot paths, print raw integers or use a lighter logging routine.
- Let the compiler help: build with
-Wall -Wextraand it warns when a conversion and argument do not match.
Quick Quiz: Predict the Output
Click each question to reveal the answer after you have tried it.
Q1. What does printf("[%-5d]", 7) print?
[7 ]. The minus flag left-justifies, so the blanks come after the digit.
Q2. What does printf("[%8.2f]", 3.14159) print?
[ 3.14]. The text “3.14” is four characters, so four blanks fill the eight-wide field.
Q3. What does printf("[%.4s]", "bootloader") print?
[boot]. Precision keeps only the first four characters.
Q4. What does printf("[%6.1f]", 99.95) print?
[ 100.0]. The stored value of 99.95 is slightly above the decimal 99.95, so it rounds up to 100.0, which is five characters, leaving one blank.
Q5. What does printf("[%05d]", 42) print?
[00042]. The zero flag pads with zeros instead of blanks.
Interview Questions
1. What is the difference between width and precision in printf?
Width is the minimum field size and only adds padding. Precision depends on the type: digits after the point for floats, maximum characters for strings, minimum digits for integers.
2. What happens if the value is wider than the width?
It is printed in full. Width never truncates.
3. What does printf return?
The number of characters printed, or a negative value if an output error occurs.
4. How do you make the width a run-time value?
Use * in the format and pass the width as an int argument just before the value, as in printf("%*d", w, value).
5. Why is printf(user_input) dangerous?
The user controls the format string, so conversions like %x or %n can read or write memory. Use printf("%s", user_input).
Summary and Key Takeaways
Remember these points
- printf width is a minimum; it pads with blanks and never cuts a value.
- Integers are right-justified by default; use
-to left-justify and0to zero-pad. - For floats, precision is the number of digits after the decimal point; the default is six.
- For strings, precision trims and width pads, and the two work together.
- Use
*to take width and precision from variables. - Match conversion and argument types, and never use user data as the format string.
Once you understand printf width and precision in C, your output stops being a stream of numbers and starts being a readable report. The three small ideas in this lecture, padding with width, rounding or trimming with precision and measuring with the return value, will serve you in every debug session. Run the programs, change the numbers, and predict each result before you press Enter.
Next we return to the input side with the scanf suppression character, a tiny star that tells scanf to read something and throw it away.
Frequently Asked Questions
What is the difference between printf width and precision in C?
Width is the minimum number of characters a value takes, padded with blanks. Precision controls detail: decimals for floats, maximum characters for strings.
Does printf width truncate a number that is too long?
No. If the value needs more room than the width allows, printf prints all of it and the field grows.
How do I print a float with two decimal places?
Use %.2f. Add a width as well, such as %8.2f, to right-align the result in eight columns.
How do I left-align text in printf?
Put a minus sign after the percent sign, for example %-10s.
Why does printf(“%.0f”, 2.5) print 2?
When a value is exactly halfway, the C library rounds to the nearest even digit. 2.5 rounds to 2 and 3.5 rounds to 4.
Why does printf not print floats on my microcontroller?
Many embedded toolchains use newlib-nano, which leaves out float formatting by default. Add the linker option -u _printf_float or print scaled integers.
Where can I learn C and embedded systems for free?
EmbeddedPathashala offers a free embedded C programming course that leads into a free embedded systems course, a free embedded Linux course, a free Linux device drivers course and a free Linux kernel development course.
Keep Going With the Free Course
You can now shape any printf output with width and precision. Continue with the next lecture on the scanf suppression character, then test yourself in the chapter quiz lab. Every lesson on EmbeddedPathashala is free, from C basics to Linux kernel and device drivers.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments