CFloating-Point Types

Floating-Point Types

Floating-point types represent real numbers with a fractional part. C provides three of them — float, double, and long double — which trade off memory usage against precision. Almost all of them rely on the IEEE 754 standard for how the bits are laid out.

float vs double vs long double

Type

Typical size

Typical precision

Common name

float

4 bytes

~6-7 significant decimal digits

single precision

double

8 bytes

~15-16 significant decimal digits

double precision

long double

8, 12, or 16 bytes (platform-dependent)

extended precision, varies by platform

extended precision

C
#include <stdio.h>

int main(void) {
    float  f = 1.0f / 3.0f;
    double d = 1.0 / 3.0;

    printf("float:  %.10f\n", f);
    printf("double: %.10f\n", d);
    return 0;
}
float:  0.3333333433
double: 0.3333333333

Notice how the float result drifts from the true value of 1/3 after only seven digits, while double stays accurate for far longer. That gap is the entire reason double is preferred whenever precision matters.

IEEE 754: a brief look under the hood

Almost every modern platform represents float and double using the IEEE 754 standard, which splits the bits into a sign bit, an exponent, and a mantissa (fraction). This scheme, similar in spirit to scientific notation, is what lets floating-point types represent both very large and very small magnitudes — but it also means most decimal fractions cannot be stored exactly in binary, the same way 1/3 cannot be written exactly with a finite number of decimal digits.

The classic precision problem

C
#include <stdio.h>

int main(void) {
    double a = 0.1;
    double b = 0.2;
    double sum = a + b;

    printf("%.17f\n", sum);          /* not exactly 0.3! */
    printf("%d\n", sum == 0.3);       /* prints 0: they are NOT equal */
    return 0;
}
0.30000000000000004
0

0.1 and 0.2 cannot be represented exactly in binary floating-point, so their sum is a value extremely close to, but not bit-for-bit identical to, 0.3. This is not a bug in C — it is an inherent property of binary floating-point representation shared by virtually every mainstream programming language.

Never compare floats with == directly
Because floating-point arithmetic accumulates tiny rounding errors, directly comparing two floating-point values with `==` is unreliable and a very common source of subtle bugs. Instead, check whether the difference between the two values is smaller than a small tolerance value, often called an epsilon.

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

int nearly_equal(double a, double b, double epsilon) {
    return fabs(a - b) < epsilon;
}

int main(void) {
    double sum = 0.1 + 0.2;
    if (nearly_equal(sum, 0.3, 1e-9)) {
        printf("Close enough to 0.3\n");
    }
    return 0;
}
Close enough to 0.3
When to use float vs double
double is the default choice
In most C code, `double` is the natural default for real numbers — it's what floating-point literals like `3.14` are by default, it's what the math library functions in `<math.h>` operate on, and its extra precision avoids many subtle rounding surprises. Reach for `float` only when memory is genuinely tight — large arrays on embedded systems, graphics buffers, or GPU data — where halving the storage per value matters more than precision.
  • float (~7 digits), double (~16 digits), and long double (extended, platform-dependent) trade memory for precision.

  • Most platforms use IEEE 754 binary representation for float and double.

  • Many decimal fractions, like 0.1, cannot be represented exactly in binary floating-point.

  • Never compare floating-point values with ==; use an epsilon-based comparison instead.

  • Default to double unless you have a concrete memory constraint pushing you toward float.