Decimal Precision in C
Why Decimal Precision Matters
When working with real numbers in C, precision determines how many digits after the decimal point can be represented and stored accurately.
Because computers store numbers in binary, not all decimal fractions can be represented exactly. This leads to rounding errors.
Precision of float vs double vs long double
• **float** → typically 4 bytes, about 6–7 digits of precision
• **double** → typically 8 bytes, about 15–16 digits of precision
• **long double** → often 8/12/16 bytes, about 18–19 digits on platforms with extended precision (implementation-dependent)
`double` is generally preferred for numeric computations when accuracy is important. For **money/finance**, prefer fixed-point or integer cents instead of binary floating-point.
Type | Size (typical) | Digits of Precision (approx) |
---|---|---|
float | 4 bytes | 6–7 |
double | 8 bytes | 15–16 |
long double | 8/12/16 bytes (varies) | 18–19 (varies by platform) |
Controlling Output Precision
When printing decimal values, you can control the number of digits displayed using **format specifiers** in `printf`.
• `%.2f` → prints 2 digits after the decimal
• `%.6f` → prints 6 digits after the decimal
This does not change the actual stored value, only the printed output.
#include <stdio.h>
int main(void) {
double pi = 3.141592653589;
printf("Default: %f\n", pi); // default precision is 6 after the decimal
printf("2 decimals: %.2f\n", pi);
printf("6 decimals: %.6f\n", pi);
return 0;
}
Default: 3.141593 2 decimals: 3.14 6 decimals: 3.141593
Precision Errors
Due to binary representation, some decimal values cannot be represented exactly.
For example, `0.1 + 0.2` might not equal exactly `0.3` but something very close to it.
This is normal in floating-point arithmetic and must be handled carefully in critical applications.
#include <stdio.h>
int main(void) {
double a = 0.1, b = 0.2, c = a + b;
printf("a+b = %.17f\n", c);
printf("0.3 = %.17f\n", 0.3);
printf("Equal? %s\n", (c == 0.3) ? "yes" : "no");
return 0;
}
a+b = 0.30000000000000004 0.3 = 0.29999999999999999 Equal? no
Comparing with a Tolerance (Epsilon)
To compare floating-point values, check whether their difference is within an absolute or relative tolerance.
#include <stdio.h>
#include <float.h>
#include <math.h>
int main(void) {
double a = 0.1, b = 0.2, c = a + b;
double target = 0.3;
double tol = 1e-12; // choose based on your value scales
if (fabs(c - target) < tol) {
printf("Close enough (abs).\n");
} else {
printf("Not close (abs).\n");
}
// Example relative tolerance
double rel_tol = 10 * DBL_EPSILON * fmax(fabs(target), 1.0);
if (fabs(c - target) <= rel_tol) {
printf("Close enough (rel).\n");
} else {
printf("Not close (rel).\n");
}
return 0;
}
Using <float.h> for Limits
`
• `FLT_DIG`, `DBL_DIG`, `LDBL_DIG` → number of decimal digits of precision
• `FLT_EPSILON`, `DBL_EPSILON`, `LDBL_EPSILON` → smallest `x` such that `1.0 + x != 1.0` for each type
#include <stdio.h>
#include <float.h>
int main(void) {
printf("FLT_DIG=%d, DBL_DIG=%d, LDBL_DIG=%d\n", FLT_DIG, DBL_DIG, LDBL_DIG);
printf("FLT_EPSILON=%.20g\n", FLT_EPSILON);
printf("DBL_EPSILON=%.20g\n", DBL_EPSILON);
printf("LDBL_EPSILON=%.20Lg\n", LDBL_EPSILON);
return 0;
}
Best Practices
• Use `float` when memory is limited and precision is not critical (e.g., graphics).
• Use `double` by default for most real number calculations.
• Use `long double` only if your platform provides extra precision (check `LDBL_DIG`).
• Control formatting in output using specifiers like `%.2f` and `%.*f`.
• For financial calculations, prefer fixed-point or integer cents instead of binary floating-point.