Printing Numeric Values

Summary: The printf format string may hold one or more format specifiers.

We've seen how printf requires at least one string (or a pointer to a string). To print variables and values, place a comma and the name of the variable or value after the format string. Then, within the format string, include a format specification. See Table 11.2.

Table 11.2 Common Format Specifications

Specification Format

%c Print a character
%d Print a decimal integer
%f Print a floating-point number
%i Print a decimal integer (same as %d)
%s Print a string
%u Print an unsigned integer
%x Print in hexadecimal format

The percent sign (%) always marks the beginning of a format specification. The letters c, d, f, i, s, u, and x are called the “type.” Between the percent sign and the type, you may include optional specifications for flags, width, or precision values.

At the very least, you must include the type, as in the program below:

/* NFORMAT.C: Print numbers and a string. */

#include <stdio.h>

#include <string.h>

main()

{

int a = -765,

b = 1,

c = 44000,

d = 33;

float e = 1.33E8,

f = -0.1234567,

g = 12345.6789,

h = 1.0;

char i[80];

strcpy( i, "word 1, word 2, word 3, word 4, word 5" );

printf( "Unformatted:\n%d %d %d %d\n", a, b, c, d );

printf( "%f %f %f %f\n", e, f, g, h );

printf( "%s\n", i );

}

The output looks like this:

Unformatted:

-765 1 -21536 33

133000000.000000 -0.123457 12345.678711 1.000000

word 1, word 2, word 3, word 4, word 5

If you carefully compare NFORMAT.C with its output, you'll notice some unexpected results. For example, the variable c, which was initialized to 44000, has somehow changed to –21536.

The %d format specification applies to signed integers in the range –32,768 to +32,767. The value of c (44,000) is outside that range, but still within the realm of unsigned integers, which can hold values up to +65,535. The proper format specification would be %u (where u represents the unsigned type).

Two of the floating-point values have changed, too. The %f specification defaults to 6 digits of precision to the right of the decimal point. The value of f (.1234567) is therefore rounded off to a precision of 6 digits: .123457. Also, the limitations of floating-point accuracy transform the value of g from 12,345.6789 to 12,345.678711. If you modify the program, changing the float declarations to double, the second problem disappears. The variable g prints correctly as 12,345.67.

Between the% and the type character, you may include two numbers separated by a period. The first number is called the “width;” the second is the “precision.” The width and precision affect integers, floating-point numbers, and strings in different ways. For example, we could specify a width of 2 and precision of 3 for each of the above variables:

printf( "\nWidth 2, Precision 3:\n" );

printf( "%2.3d %2.3d %2.3u %2.3d\n", a, b, c, d );

printf( "%2.3f %2.3f %2.3f %2.3f\n", e, f, g, h );

printf( "%2.3s\n", i );

(Note that the variable c has a format specifier of %2.3u instead of %2.3d.) The screen displays the following lines:

Width 2, Precision 3:

-765 001 44000 033

133000000.000 -0.123 12345.679 1.000

wor

For integers, the precision of 3 causes at least 3 digits to print, preceded by leading zeros. For floating-point numbers, the precision of 3 truncates fractions to 3 digits to the right of the decimal point. For strings, the precision of 3 causes only 3 characters to print. The string output is truncated to the right. Numbers are never truncated, however.

We can change the width to 8 and the precision to 1:

printf( "\nWidth 8, Precision 1:\n" );

printf( "%8.1d %8.1d %8.1u %8.1d\n", a, b, c, d );

printf( "%8.1e %8.1f %8.1f %8.1f\n", e, f, g, h );

printf( "%8.1s\n", i );

We made an additional modification by printing the variable e as an %e type instead of an %f type. This prints the value of e (1.33E8) in exponential format:

Width 8, Precision 1:

-765 1 44000 33

1.3e+008 -0.1 12345.7 1.0

w

The width controls the printing area: all 3 variable types are printed in fields 8 characters wide. The precision of 1 affects different data types in different ways: the integers print at least 1 digit; the floating-point numbers print only the first number to the right of the decimal point; and the string prints as the first character only. Each value prints flush right in its field.

Between the % and the width, you may also insert a flag. The plus flag (+), for example, forces numbers to print with a leading sign:

printf( "\nForced signs, Width 10, Precision 2:\n" );

printf( "%+10.2d %+10.2d %+10.2u %+10.2d\n", a, b, c, d );

printf( "%+10.2e %+10.2f %+10.2f %+10.2f\n", e, f, g, h );

printf( "%+10.2s\n", i );

Note that the plus flag has no effect on strings or on unsigned integers:

Forced signs, Width 10, Precision 2:

-765 +01 44000 +33

+1.33e+008 -0.12 +12345.68 +1.00

wo

Another flag is the number 0, which forces leading zeros to print within the limits of the width. If you only specify the width, the system default is used for the precision. You can use the type %x to represent hexadecimal; it displays the letters a–f in lowercase. If you prefer uppercase, you can use %X instead.

printf( "\nHexadecimal, Forced Zeros, Width 6:\n" );

printf( "%06x %06x %06x %06x\n", a, b, c, d );

The printf statements above display these lines:

Hexadecimal, Forced Zeros, Width 6:

00fd03 000001 00abe0 000021

For strings, the width and precision specifiers describe the field width and the number of characters printed. Note the minus sign in the final line, which forces the truncated string to print from the left:

printf( "\nWidth 40, Precision 10:\n" );

printf( "%40.10s\n", i );

printf( "\nWidth 40, Precision 20:\n" );

printf( "%40.20s\n", i );

printf( "\nFlush left, Width 40, Precision 20:\n" );

printf( "%-40.20s\n", i );

The lines are displayed on the screen as follows:

Width 40, Precision 10:

word 1, wo

Width 40, Precision 20:

word 1, word 2, word

Flush left, Width 40, Precision 20:

word 1, word 2, word