3
if (std::abs(double1 - double2) < std::numeric_limits<double>::epsilon())
  std::cout<<"Equal";
else
  std::cout<<"Not equal";

Is this code with modern C++11/14/17/21 is still the way we should compare float and doubles, or now it's ok just to write

if (double1 == double2)

And compiler will handle the epsilon issue for us?

BTW: is it better to write < or <= when checking against epsilon?

Bruice
  • 543
  • 3
  • 11
  • 2
    this isnt about modern or not modern, but either way can be right or wrong, depending on what you actually want. `==` comares for equality, also `double`s, you just need to know if thats really want you want – 463035818_is_not_an_ai Jun 04 '21 at 18:38
  • 2
    Yes, it is still the way to do it. For why, see: https://stackoverflow.com/questions/588004/is-floating-point-math-broken – NathanOliver Jun 04 '21 at 18:39
  • 4
    ***And compiler will handle the epsilon issue for us?*** No it will still do what you tell it. – drescherjm Jun 04 '21 at 18:40
  • ***BTW: it's better write < or <= when checking against epsilon?*** The choice is likely irrelevant depending on what you are using for epsilon – drescherjm Jun 04 '21 at 18:41
  • @NathanOliver ye< I know about this issue, but all things I've googled about that was like 8+ years ago. So I'm started to wonder if something has changed – Bruice Jun 04 '21 at 18:42
  • @NathanOliver: If anything, that test is too strict; `epsilon` is the difference between `1.0` and the next representable value above it, but with complicated floating point math (or higher magnitude math), the compounding errors may make the result more than just an `epsilon` or two away; ideally you define some relative or absolute tolerance explicitly, in the model of [Python's `math.isclose`](https://docs.python.org/3/library/math.html#math.isclose). – ShadowRanger Jun 04 '21 at 18:44
  • 2
    Nope, no changes. One of the main goals of C++ is performance and doing an epsilon check is more expensive then not doing it, so the default operators aren't going to. If you want to compare against an epsilon, you have to do that yourself. – NathanOliver Jun 04 '21 at 18:44
  • @ShadowRanger Or not strict enough? Consider when d1 is epsilon*1e-10 and d2 is d1*2. Should they be considered the same, even though their difference is (much) less than epsilon. – Adrian Mole Jun 04 '21 at 18:47
  • 4
    If you want to check for equality, `==` is and has always been what you need to use. If you want to check for approximate equality you need to first decide what you mean by that (are `100000000` and `100000001` approximately equal? are `0.000001` and `0.0000000001` approximately equal? This really depends on context). – chtz Jun 04 '21 at 18:47
  • If you radically change the behaviour of a common operator like `==` the effects on old code are staggering. Besides the war over the correct epsilon value to force onto all C++ programmers would still be fought by our children's children's children. Assuming we don't wind up nuking everybody over something trivial like wearing masks. – user4581301 Jun 04 '21 at 18:51
  • 1
    *"modern C++11/14/17/21"* -- Is this supposed to be an oxymoron? A 10-year-old standard like C++11 is not exactly something I would call "modern". C++14 is not much better, but I would accept C++17 as "modern" since C++20 support by major compilers is incomplete (or "experimental", as gcc puts it). – JaMiT Jun 04 '21 at 19:02
  • some standard algorithms and containers require `==` to mean equality and potentially you get undefined behavior when it isnt. The epsilon comparison is the right thing to do in some cases, but not always – 463035818_is_not_an_ai Jun 04 '21 at 19:02
  • Note that because you use `std::numeric_limits::epsilon()`, this can only be true if the two doubles were already equal or if they are both between -1 and 1, since the minimum (nonzero) difference between two double values > 2 or < -2 is more than epsilon (and between 1 and 2 is exactly epsilon). A more sensible value could be something like `0.0001`, depending entirely on the expected range of values for the doubles and the wanted tolerance. – Artyer Jun 04 '21 at 19:27
  • Using a comparison based on [`std::nextafter`](https://en.cppreference.com/w/cpp/numeric/math/nextafter) or its cousins would be much more sensible than using epsilon. – Adrian Mole Jun 04 '21 at 19:31

1 Answers1

5

Is this code with modern C++11/14/17/21 is still the way we should compare float and doubles, or now it's ok just to write if (double1 == double2) And compiler will handle the epsilon issue for us?

Both approaches function the same in modern C++ as they did in early C++.

Both approaches are also flawed.

  • Using == assumes that your code has accounted for any floating point rounding errors, and it's very rare/difficult for code to do that.

  • Comparing against epsilon assumes that a reasonable amount of rounding error will be less than the constant epsilon, and that is very likely a wrong assumption!

    • If your numbers have magnitude greater than 2.0, your epsilon trick will be no different from direct comparison, and have the same flaws. Regardless of whether you use < or <=.
    • If your numbers have the same sign and a magnitude smaller than epsilon, your epsilon trick will say they are always equal, even if one is hundreds of times larger than the other. They would both be equal to zero, too.

A wise approach may be to avoid writing code that depends on whether floating point numbers are equal. Instead test if they are relatively close, by some factor.

The code below will test whether two numbers are within about 0.01% of each other. Regardless of their scale.

const auto relative_difference_factor = 0.0001.    // 0.01%
const auto greater_magnitude = std::max(std::abs(double1),std::abs(double2));

if ( std::abs(double1-double2) < relative_difference_factor * greater_magnitude )
  std::cout<<"Relatively close";
else
  std::cout<<"Not relatively close";
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180