Francesco Paolo Lezza

The problem

Floating-point numbers in C follow the IEEE 754 standard. You have three types:

float f = 0.1f;        // 32-bit, single precision
double d = 0.1;        // 64-bit, double precision
long double ld = 0.1L; // 80 or 128-bit, platform-dependent

The catch is that some decimal fractions (like 0.1, or π) cannot be represented exactly in binary. Same reason 1/3 in decimal gives you 0.333… forever: change the base, change what’s “exact”.

Practical result:

#include <stdio.h>
int main() {
    double pi = 3.1415926535897932384626433832795028841971;
    double y = 0.1;
    printf("%.30f\n", pi + y);
    // input:   3.1415926535897932384626433832795028841971
    // output:  3.241592653589793204815805438557
}

That tiny difference at the twentieth decimal place is not noise: it’s structural.

When you need exact decimal precision

C has no built-in decimal type. But you can reach for GMP/MPFR:

#include <gmp.h>
int main(void) {
    const char *pi_str = "3.14159265358979323846"
                     "26433832795028841971";
    mpf_set_default_prec(256);
    mpf_t pi;
    mpf_init_set_str(pi, pi_str, 10);
    gmp_printf("%.50Ff\n", pi);
    // output: 3.14159265358979323846264338327950288419710000000000
    mpf_clear(pi);
    return 0;
}

Exact to as many digits as you want, but you own the rounding.

Fixed-point: handcrafted but effective

No external libraries, fixed precision: use integers with a scaling factor.

#include <stdio.h>
#define SCALE 65536  // 2^16

int main() {
    double pi = 3.1415926535897932384626433832795028841971;
    int pi_fixed = (int)(pi * SCALE);
    double pi_value = (double)pi_fixed / SCALE;
    printf("%.16f\n", pi_value);
    // output: 3.1415863037109375
    // (limited by 16-bit fractional precision)
}

No heap, no dependencies. Works great for audio, graphics, embedded.

In short

Approach When to use it
float / double Scientific computing, graphics, ML
GMP/MPFR Arbitrary precision, finance, cryptography
Fixed-point Embedded, real-time, zero dependencies

None of them is wrong. It depends on what you’re building and how much error you can live with.


This post grew out of a contribution I made to a fork of floating-point-gui.de, an educational resource by Michael Borgwardt published under CC BY 3.0.