Floating-Point Numbers

float and double store numbers with fractional parts using binary floating point. They cover an enormous range, but most decimal fractions can only be stored approximately — so you need to know how to print them, compare them and when not to use them.

float and double

C
#include <stdio.h>

int main(void) {
    float f = 3.14159265358979f;
    double d = 3.14159265358979;
    printf("float:  %.15f\n", f);
    printf("double: %.15f\n", d);
    return 0;
}
Output
float:  3.141592741012573
double: 3.141592653589790

A float keeps about 7 significant digits; a double about 15–16. The f suffix makes a float literal — without it, 3.14 is a double. Use double unless you have a reason not to (large arrays, graphics, embedded hardware).

Approximate by nature

Just as 1/3 can't be written exactly in decimal, 0.1 can't be written exactly in binary. Small errors appear:

C
#include <stdio.h>

int main(void) {
    double a = 0.1 + 0.2;
    printf("%.17f\n", a);
    printf("%s\n", a == 0.3 ? "equal" : "not equal");
    return 0;
}
Output
0.30000000000000004
not equal

Never compare floating-point results with ==. Check whether they're close enough instead:

C
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool nearly_equal(double x, double y) {
    return fabs(x - y) <= 1e-9 * fmax(1.0, fmax(fabs(x), fabs(y)));
}

int main(void) {
    printf("%d\n", nearly_equal(0.1 + 0.2, 0.3));
    return 0;
}
Output
1

The tolerance depends on your problem; this one scales with the size of the numbers.

Money: use integers

Because of rounding, don't store money in double. Count the smallest unit — cents — in an integer, and only format it as a decimal when printing:

C
#include <stdio.h>

int main(void) {
    long long price_cents = 1999;
    int quantity = 3;
    long long total = price_cents * quantity;
    printf("Total: %lld.%02lld\n", total / 100, total % 100);
    return 0;
}
Output
Total: 59.97

Printing

SpecifierOutput for 1234.5678
%f1234.567800 (6 decimals by default)
%.2f1234.57 (rounded)
%e1.234568e+03 (scientific)
%g1234.57 (shortest sensible form)

%f prints both float and double. (For scanf it's different: %f reads a float and %lf a double — lesson 15.)

C
#include <stdio.h>

int main(void) {
    double x = 1234.5678;
    printf("%f | %.2f | %e | %g\n", x, x, x, x);
    printf("%g | %g\n", 0.00001234, 123456789.0);
    return 0;
}
Output
1234.567800 | 1234.57 | 1.234568e+03 | 1234.57
1.234e-05 | 1.23457e+08

Special values

Floating point has values for results that aren't ordinary numbers: infinity and "not a number" (NaN).

C
#include <stdio.h>
#include <math.h>

int main(void) {
    double big = 1e308 * 10;
    double nothing = sqrt(-1.0);
    printf("%f %f\n", big, -big);
    printf("%f\n", nothing);
    printf("isinf: %d, isnan: %d\n", isinf(big), isnan(nothing));
    printf("NaN == NaN? %d\n", nothing == nothing);
    return 0;
}
Output
inf -inf
nan
isinf: 1, isnan: 1
NaN == NaN? 0

A NaN isn't equal to anything, not even itself; test for it with isnan. (Integer division by zero, by contrast, is undefined behaviour and usually crashes.)

Limits

C
#include <stdio.h>
#include <float.h>

int main(void) {
    printf("double: about %d digits, max %g\n", DBL_DIG, DBL_MAX);
    printf("float:  about %d digits, max %g\n", FLT_DIG, FLT_MAX);
    printf("smallest step after 1.0: %g\n", DBL_EPSILON);
    return 0;
}
Output
double: about 15 digits, max 1.79769e+308
float:  about 6 digits, max 3.40282e+38
smallest step after 1.0: 2.22045e-16

Try it yourself

Add 0.1 to a double ten times and print the result with 17 decimals. Is it exactly 1? Then do the same with integers counting tenths.

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
0.99999999999999989
1.0