1

I found a bug in the codebase I'm working in where they check for equality of two doubles (first, second) using first == second, but these 2 variables could be NaN.

If both variables were NaN the equality would be false.

So my current solution is instead of

first == second

we use

(first == second || (std::isnan(first) && std::isnan(second))

Is there a simpler way to do this?

roulette01
  • 1,984
  • 2
  • 13
  • 26
  • lol, it might not have been a bug, just a programmer with more experience than you. – user1095108 Mar 16 '23 at 17:33
  • @user1095108 i mean it's not the intended behavior of that check in the codebase so it definitely was a bug – roulette01 Mar 16 '23 at 17:38
  • 1
    Do you want a “true” result if both items are NaNs or only if both items are the same NaN (have the same sign and payload and are both signaling or both quiet)? – Eric Postpischil Mar 16 '23 at 17:38
  • The whole point of NaN is that it's Not a Number. Code that compares two things that are supposed to be numbers should not expect that the comparison will yield `true` for two things that aren't numbers. – Jerry Coffin Mar 16 '23 at 17:39
  • @EricPostpischil I think the former. This check was done in an `operator==` check and `first, second` here are attributes of the underlying class. For some reason, the original developer chose to initialize those variables to `std::numeric_limits::quiet_NaN()` – roulette01 Mar 16 '23 at 17:40
  • comparing floating point numbers using equality gives me the shivers. – Martin York Mar 16 '23 at 18:25
  • @roulette01 yes, he was more experienced than you :) Can't you see? This is what happens if you let a skilled arithmetician go. – user1095108 Mar 16 '23 at 20:39

2 Answers2

3

First, evaluate that you actually want the condition to run if both values are NaN. NaN is a good indicator that things went terribly wrong, not a null or empty state. If the intention was to have two values that may or may not exist, you might consider changing them to std::optional<double> to make that clear. Otherwise, if you are certain, then what you've done is correct. There's no magic trick to making NaN comparisons work, and even if you did find a library that abstracted this away, it would still do exactly what you just did under the hood.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
1

Rather than use an overly generic expression:

(first == second || (std::isnan(first) && std::isnan(second))

Use a named function to start with:

inline bool areFloatPointComparablyEquivelent(double lhs, double rhs)
{
      // Add a long very detailed explanation of why you need
      // This expression (in two years you will not be here and the
      // the next maintainer may own an axe and know where you live).

      // The exact reason or expression is not that important.
      // What is important (if not trivial, obvious) is a WHY.
      return result;
}

Now use this self documenting function wherever you need to do this test (even if it is only in one place).

Martin York
  • 257,169
  • 86
  • 333
  • 562