Reading and Writing Floating-Point Numbers in C

In this lesson we will see how to print floating-point values to the console and how to read them from the keyboard in C language.

We will see how to apply the printf function to write float, double and long double values and the scanf function to read them from the keyboard.

printf and floating-point types

Now it's time to go into detail on how to print double, float and long double type values to the console.

Compared to integer types, floating-point types require more complex formatting. But let's start in order.

As we have already seen previously, to print values of different types through the printf function it is necessary to specify so-called format specifiers in the format string.

In the case of floating-point numbers there are 3 specifiers.

Definition

Format Specifiers for Floating-Point Numbers

The format specifiers for floating-point numbers are:

  • %f: prints the number in decimal notation keeping at most 6 decimal digits after the point;
  • %e: prints the number in scientific notation keeping at most 6 significant digits;
  • %g: prints the number in decimal notation if possible with 6 significant digits, otherwise uses scientific notation.

Let's examine these specifiers in detail.

Specifier f

The %f specifier is used to print a value in decimal notation. In other words, the number is printed with the point and the fractional part.

Let's see an example:

float pi = 3.14159;
printf("The value of pi is: %f\n", pi);

The result will be:

The value of pi is: 3.141590

By default the %f specifier prints 6 decimal digits. Therefore, it might happen that the number gets truncated during printing. For example:

float value = 0.000123456;
printf("The value is: %f\n", value);

The result will be:

The value is: 0.000123

As you can see, the number has been truncated to 6 decimal digits. This problem doesn't occur with the integer part. In fact, for example if we try to print the number 123456789.123456789:

float value = 123456789.123456789;
printf("The value is: %f\n", value);

The result will be:

The value is: 123456789.123457

Note that the integer part has been printed in its entirety. Conversely, the fractional part has been truncated to 6 decimal digits and rounded at the sixth digit.

To overcome this problem, the C language provides another format specifier for printing in decimal notation.

To summarize:

Definition

Specifier f for the printf function

The %f specifier tells the printf function to print a floating-point number in decimal notation.

The result will have the following appearance:

<integer part>.<fractional part>

In detail, its default behavior is as follows:

  • The integer part is printed in its entirety
  • The fractional part is truncated and rounded to 6 decimal digits.

Specifier e

The %e specifier is used to print a value in scientific notation. Therefore the number will always be represented with a fractional number and an exponent. The fractional number will always have one non-zero digit before the point and the exponent will be modified accordingly.

For example, if we take the number \pi:

float pi = 3.14159;
printf("The value of pi is: %e\n", pi);

The result will be:

The value of pi is: 3.141590e+00

Let's observe some things:

  • Using scientific notation, the number should be written as: 3.141590 \cdot 10^0. In C, as in many other languages, the 10 is omitted, as it is implicit, and replaced by e. Therefore, the number is written as 3.141590e+00;
  • the number \pi already has a non-zero digit before the point. This is why the exponent is 0.

Let's try to print a number like 0.000123456, therefore a number with a digit before the point equal to zero:

float value = 0.000123456;
printf("The value is: %e\n", value);

The result will be:

The value is: 1.234560e-04

As you can see, the number has been printed in scientific notation with exponent -4 and the non-zero digit before the point.

Similarly, if we try to print the number 123.456789:

float value = 123.456789;
printf("The value is: %e\n", value);

The result will be:

The value is: 1.234568e+02

The point has been moved 2 positions to the left and the exponent has been incremented by 2.

In any case, and we can notice it from the last example, the number will be printed always rounded to 6 significant digits.

To summarize:

Definition

Specifier e for the printf function

The %e specifier tells the printf function to print a floating-point number in scientific notation.

The result will have the following appearance:

<fractional part>e<exponent>

In detail, its default behavior is as follows:

  • The fractional part is truncated and rounded to 6 significant digits;
  • The exponent is calculated so that the fractional part has only one non-zero digit before the point.

Specifier g

The %g specifier prints numbers in a hybrid format between %f and %e. This format specifier automatically chooses between %f and %e based on the value to be printed.

In detail, the %g specifier tries to always keep 6 significant digits. If the number can be represented with only 6 significant digits, it will be printed in decimal notation. Otherwise, it will be printed in scientific notation.

For example, if we try to print the number \pi:

double pi = 3.14159;
printf("The value of pi is: %g\n", pi);

The result will be:

The value of pi is: 3.14159

If we try to print the number 0.000123456 instead:

double value = 0.000123456;
printf("The value is: %g\n", value);

The result will be:

The value is: 1.234560e-04

While for pi decimal notation was used, the second number was printed in scientific notation. This is because it is not representable with only 6 significant digits using decimal notation. Therefore, the printf function chose to print the number in scientific notation.

Similarly, if we try to print the number 123456789.123456789:

double value = 123456789.123456789;
printf("The value is: %g\n", value);

The result will be:

The value is: 1.23457e+08

In this case too, scientific notation was used.

To summarize:

Definition

Specifier g for the printf function

The %g specifier tells the printf function to print a floating-point number according to the following rules:

  • If the number can be represented with 6 significant digits or less in decimal notation, then it will be printed in decimal notation;
  • Otherwise, it will be printed in scientific notation.

Modifying the number of decimal digits to print

As we have seen, the three format specifiers %f, %e and %g print at most 6 decimal digits or 6 significant digits.

This behavior, obviously, is not suitable for all cases. Fortunately, the printf function provides a way to modify the number of decimal digits to print.

The general syntax for formatting floating-point numbers is as follows:

%[flags][width][.precision]{f|e|g}

In the syntax above the elements f, e and g are mandatory and work as described above.

The other elements are described below:

  • width: specifies the minimum number of characters with which to print the number. If the number needs fewer characters, it will be filled with spaces. If the number needs more characters, it will be printed with all the necessary characters. Note that in the character count the point, sign and exponent must also be considered.

    For example, if we try to print the number \pi with a width of 10 characters:

    double pi = 3.14159;
    printf("The value of pi is: %10f\n", pi);
    

    The result will be:

    The value of pi is:   3.141590
    

    As you can notice, a zero has been added to reach the 6 decimal digits. In doing so, the number needs 8 characters to be printed, including the point. Therefore, 2 spaces have been added to reach 10 characters.

    If, instead, we try to print a number like 123456789.123456789 with a width of 10 characters:

    double value = 123456789.123456789;
    printf("The value is: %10f\n", value);
    

    The result will be:

    The value is: 123456789.123457
    

    First, the number has been truncated to 6 decimal digits, as indicated by the %f specifier. In this way the number needs 15 characters to be printed which exceed the value of 10. Therefore, the number has been printed with all the necessary characters.

    Therefore if more characters are needed than specified, the width will be ignored.

  • precision: specifies the number of digits to insert after the point for the f and e specifiers. While, for the g specifier it indicates the number of significant digits to use. Note that the point must always be inserted before the precision.

    For example, if we try to print the number \pi with a precision of 2 decimal digits:

    double pi = 3.14159;
    printf("The value of pi is: %.2f\n", pi);
    

    The result will be:

    The value of pi is: 3.14
    

    As you can notice, the number has been truncated to 2 decimal digits.

    If we try to print the number 123456789.123456789 with a precision of 4 decimal digits:

    double value = 123456789.123456789;
    printf("The value is: %.4f\n", value);
    

    The result will be:

    The value is: 123456789.1235
    

    If we had, instead, used the g specifier:

    double value = 123456789.123456789;
    printf("The value is: %.4g\n", value);
    

    The result would be as follows:

    The value is: 1.235e+08
    

    In this case scientific notation was used and the number was truncated to 4 significant digits.

  • flags: is a set of characters that can be used to modify the formatting of the number. There are 3 flags.

    1. +: Indicates that the sign of the number must always be printed. Even if the number is positive.
    2. -: Indicates that the number must be left-aligned. By default, numbers are right-aligned.
    3. 0: Indicates that numbers must be filled with zeros. By default, numbers are filled with spaces.

    Let's see some examples. If we try to print the number \pi with the sign always printed:

    double pi = 3.14159;
    printf("The value of pi is: %+f\n", pi);
    

    The result will be:

    The value of pi is: +3.141590
    

    Left alignment, instead, makes sense if we use the width. For example, if we try to print the number \pi with a width of 10 characters, left-aligned and with only 2 decimal digits:

    double pi = 3.14159;
    printf("The value of pi is: %-10.2fcontinuation of the line\n", pi);
    

    The result will be:

    The value of pi is: 3.14      continuation of the line
    

    As you can notice, the number has been left-aligned and filled with spaces.

    Finally, if we try to print the number \pi with a width of 10 characters, filled with zeros and with 4 decimal digits:

    double pi = 3.14159;
    printf("The value of pi is: %010.4f\n", pi);
    

    The result will be:

    The value of pi is: 000003.1416
    

    In this case instead of spaces on the left, zeros have been inserted.

To summarize:

Definition

Formatting of Floating-Point Numbers

The formatting of floating-point numbers occurs through the format string of the printf function according to the following syntax:

%[flags][width][.precision]{f|e|g}

Where:

  • flags: indicates any modifications to the formatting of the number. The available flags are:
    • +: indicates that the sign of the number must always be printed;
    • -: indicates that the number must be left-aligned;
    • 0: indicates that numbers must be filled with zeros and only makes sense if the width is specified;
  • width: specifies the minimum number of characters with which to print the number including the point, sign and exponent;
  • precision: specifies the number of decimal digits to print for the f and e specifiers and the number of significant digits for the g specifier;

Forcing the type of the floating-point value

All the specifiers specific for floating-point numbers shown so far make the implicit assumption that the passed value is of type double.

Therefore, even if we pass a float value or a long double, internally the compiler will convert it to double.

If, instead, we want to force the type of the passed value, we can use type modifiers. As in the case of integers, it is a matter of adding a prefix in front of the f, e or g specifiers. In the case of floating-point values there is only one prefix:

  • L: indicates that the passed value is of type long double.

Therefore, for example, if we want to print a long double value with 10 decimal digits:

long double pi = 3.14159265358979323846L;
printf("The value of pi is: %.10Lf\n", pi);

The result will be:

The value of pi is: 3.1415926536

However, there is the l prefix which is ignored as it indicates a value of type double which is the default type.

To summarize:

Definition

Forcing the Type of the Floating-Point Value

Normally, floating-point values passed to the printf function are converted to double.

To force the type of the floating-point value passed to the printf function, it is possible to use the L prefix in front of the format specifiers f, e and g.

This prefix indicates that the passed value is of type long double.

Reading floating-point values with scanf

Now that we have seen how to print floating-point values through the printf function it is time to see how to read floating-point values.

As for integers, we can use the scanf function using the correct format specifiers.

In particular, the specifiers are as follows:

  • %f: to read a floating-point value of type float;
  • %lf: to read a floating-point value of type double;
  • %Lf: to read a floating-point value of type long double.

When we use scanf to read a floating-point value, we can enter the number both in decimal notation and in scientific notation.

For example, if we want to read a double value:

#include <stdio.h>

int main() {
    double value;
    printf("Enter a value: ");
    scanf("%lf", &value);
    printf("You entered the value: %f\n", value);
    return 0;
}

Running the program, we can enter a number both this way:

Enter a value: 3.14159
You entered the value: 3.141590

And this way:

Enter a value: 1.234e-02
You entered the value: 0.012340

In any case it is of fundamental importance to insert the correct specifier suitable for the type.

We notice some fundamental differences compared to the printf function regarding format specifiers:

  • There are no three different specifiers for floating-point values for the scanf function depending on the notation. Therefore, %f reads both values in decimal notation and in scientific notation;
  • Unlike the printf function, in the case of scanf the %f specifier reads a float and not a double. To read a double it is necessary to use the %lf specifier.

To summarize:

Definition

Reading Floating-Point Values with scanf

To read floating-point values with the scanf function it is necessary to use the following format specifiers:

  • %f: to read a floating-point value of type float;
  • %lf: to read a floating-point value of type double;
  • %Lf: to read a floating-point value of type long double.
Note

Format Specifier %f

The format specifier %f represents one of those rare cases in which the data type passed to the scanf function is different from the one passed to the printf function.

In fact, while %f for the printf function represents a double, for the scanf function it represents a float.

In Summary

In this lesson we have seen how to print and read floating-point values in C language.

To print floating-point values, we used the three format specifiers %f, %e and %g. We saw how to modify the number of decimal digits to print and how to force the type of the passed value.

The general format for formatting floating-point numbers is:

%[flags][width][.precision]{f|e|g}

where:

  • flags: indicates any modifications to the formatting of the number;
  • width: specifies the minimum number of characters with which to print the number;
  • precision: specifies the number of decimal digits to print for the f and e specifiers and the number of significant digits for the g specifier.

To read floating-point values, we used the specifiers %f, %lf and %Lf to read respectively float, double and long double.