How to Compare Floating-Point Numbers in C++
Short answer: do not use ==. Compare the difference against a tolerance:
#include <cmath>
#include <limits>
bool almostEqual(double a, double b) {
double diff = std::fabs(a - b);
double scale = std::max(std::fabs(a), std::fabs(b));
return diff <= std::numeric_limits<double>::epsilon() * scale * 4;
}
The Problem, Demonstrated
#include <iostream>
#include <iomanip>
int main() {
double a = 0.1 + 0.2;
std::cout << (a == 0.3) << '\n'; // 0 — false!
std::cout << std::setprecision(20) << a << '\n'; // 0.30000000000000004441
}
This is not a C++ bug, and it is not your compiler. Every language using IEEE 754 doubles behaves the same way — Python, JavaScript, Java, C#.
Why It Happens
Binary fractions can represent halves, quarters and eighths exactly. They cannot represent one tenth, for the same reason decimal cannot represent one third — writing 0.333… never terminates.
So 0.1 in a double is not 0.1. It is the nearest representable binary value, which is very slightly off. Add two such approximations and the errors compound into something visible.
Integers up to 2⁵³ are stored exactly, which is why this surprise only appears with fractions.
The Naive Fix, and Why It Is Not Enough
// works for small numbers, fails for large ones
bool almostEqual(double a, double b) {
return std::fabs(a - b) < 0.00001;
}
A fixed tolerance breaks down as magnitudes grow. At around 10¹⁶, consecutive doubles are more than 1 apart, so two genuinely different values can never differ by less than 0.00001 — and two identical-in-practice values may differ by far more.
The Scaled Tolerance Version
#include <algorithm>
#include <cmath>
#include <limits>
bool almostEqual(double a, double b, double factor = 4.0) {
double diff = std::fabs(a - b);
if (diff <= std::numeric_limits<double>::min()) return true; // both ~0
double scale = std::max(std::fabs(a), std::fabs(b));
return diff <= std::numeric_limits<double>::epsilon() * scale * factor;
}
int main() {
std::cout << almostEqual(0.1 + 0.2, 0.3) << '\n'; // 1
std::cout << almostEqual(1e16, 1e16 + 1) << '\n'; // 1 — indistinguishable
std::cout << almostEqual(1.0, 1.1) << '\n'; // 0
}
The tolerance now grows with the numbers, so it behaves sensibly at any magnitude. The factor controls how many representable steps apart you will still call “equal” — 4 is a reasonable default for ordinary arithmetic.
Note the early return: when both values are essentially zero, the scaled tolerance collapses to zero and would reject them, so that case needs handling separately.
Comparing Against Zero
Zero is the one case where a scaled tolerance cannot work, because there is no magnitude to scale by. Use a small absolute tolerance chosen for your problem:
bool isZero(double x, double tolerance = 1e-12) {
return std::fabs(x) < tolerance;
}
What counts as “small” depends entirely on your domain — 1e-12 is meaningless if you are working in nanometres.
When == Is Actually Fine
Exact comparison is correct when no arithmetic has happened:
double x = 3.14;
if (x == 3.14) { ... } // fine — same literal, no computation
double v = getSentinel();
if (v == std::numeric_limits<double>::infinity()) { ... } // fine
It is arithmetic — especially repeated arithmetic in loops — that accumulates the error.
The NaN Exception
Not-a-number is never equal to anything, including itself:
double nan = std::numeric_limits<double>::quiet_NaN();
std::cout << (nan == nan) << '\n'; // 0
std::cout << std::isnan(nan) << '\n'; // 1 — the correct test
That self-inequality is actually the classic trick for detecting NaN without <cmath>.
Avoid Accumulating Error in Loops
// error grows with every iteration
for (double t = 0.0; t != 1.0; t += 0.1) { ... } // may never terminate!
// count in integers instead
for (int i = 0; i < 10; ++i) {
double t = i * 0.1;
}
The first loop can run forever because t never lands exactly on 1.0. Counting with an integer and deriving the double each time keeps the error from compounding.
When to Use a Different Type Entirely
For money, do not use double at all. Store cents as an integer:
long long cents = 1999; // $19.99 — exact
Floating point is for measurement and science, where small relative error is acceptable. Currency needs exact decimal arithmetic.
Quick Reference
| Goal | Code |
|---|---|
| Compare two doubles | almostEqual(a, b) from above |
| Compare against zero | std::fabs(x) < 1e-12 |
| Detect NaN | std::isnan(x) |
| Detect infinity | std::isinf(x) |
| Machine epsilon | numeric_limits<double>::epsilon() |
| Money | use integer cents, not double |
Take Your C++ Further
If you want precision, types and the rest of the fundamentals explained properly, the C++ Better Explained Ebook covers them in plain English. Just $19.
👉 Get the C++ Better Explained Ebook — $19
Related Articles
- Maximum Value of a double in C++ — range, epsilon and infinity.
- C++ float vs double — which to use and why.
- INT_MAX and INT_MIN in C++ — the integer limits.
- Integer Division in C++ — the other classic arithmetic surprise.
- C++ Variables and Data Types — the wider type picture.