3

I was writing a constexpr bool isfinite(Float) function because I'm not on C++23 yet and so my std::isfinite isn't constexpr. But CI said MSVC didn't like something.

In general, nan should compare false with anything with all comparisons other than != which should always be true: https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN

After some digging, here's the simplest form of the problem:

#include <cassert>
#include <limits>

// Stating assumptions:
static_assert(std::numeric_limits<float>::is_iec559);
static_assert(std::numeric_limits<float>::has_quiet_NaN);
static_assert(std::numeric_limits<float>::has_infinity);
static_assert(std::is_same_v<decltype(NAN), decltype(INFINITY)>);
static_assert(std::is_same_v<float, decltype(INFINITY)>);


int main() {
    static_assert(INFINITY == std::numeric_limits<float>::infinity()); // Obviously!
    assert(INFINITY == std::numeric_limits<float>::infinity()); // Obviously.
    assert(not (NAN < std::numeric_limits<float>::infinity())); // Nan always compares false, so good.
    assert(NAN < INFINITY); // What?! This shouldn't pass!
    static_assert(NAN < INFINITY); // Neither should this!
    static_assert(NAN < std::numeric_limits<float>::infinity()); // Neither should this!
}

https://godbolt.org/z/dsqhcz18E

What the heck is going on? Am I into implementation-defined behavior?

With clang I get the errors I'd expect: https://godbolt.org/z/cvKv8ssax and same with gcc: https://godbolt.org/z/Moen3x1ov

Ben
  • 9,184
  • 1
  • 43
  • 56
  • 2
    Adding [`/fp:strict`](https://godbolt.org/z/d8evPxv69) results in the desired runtime behavior, but not on compile time. [This post](https://stackoverflow.com/q/72395161/3740047) is related. EDIT: Using `std::numeric_limits::quiet_NaN()` instead of `NAN` also provides the [correct runtime behavior without `/fp:strict`](https://godbolt.org/z/zYsvhzcne). – Sedenion Nov 29 '22 at 18:32
  • 1
    Also, possibly related to the way the MSVC system headers define `NAN`: `#define NAN (-(float)(INFINITY * 0.0F))`? – Adrian Mole Nov 29 '22 at 18:37
  • 1
    Regarding the compile time issue: There is [this post](https://stackoverflow.com/q/66420107/3740047) and also [this](https://developercommunity.visualstudio.com/t/Compile-time-NaN-comparison/1626139) and [this](https://developercommunity.visualstudio.com/t/nan-nan-is-constant-folded-to-true-but-should-prob/445462) bug report. – Sedenion Nov 29 '22 at 18:50
  • 1
    Related: https://stackoverflow.com/questions/38798791/nan-comparison-rule-in-c-c – lorro Nov 29 '22 at 18:51
  • 1
    Standard specifies suprisingly little about NaN unless iec559 applies (which can be checked). – lorro Nov 29 '22 at 18:57

1 Answers1

2

Am I into implementation-defined behavior?

From C++ perspective: Yes, sort of. The non-finite floating point comparisons are not defined by the C++ standard. That said, static_assert(std::numeric_limits<float>::is_iec559) brings in another standard into consideration.

From IEC 559 aka IEEE-754 perspective: No, the comparisons are defined by this standard, so they aren't implementation defined.

What the heck is going on?

Looks like a bug in the language implementation.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • So, it sounds like I'm right that once I `static_assert(std::numeric_limits::is_iec559)`, I should be able to expect that nan compares `false` against any other `float` with all comparisons except `!=` which returns `true`. And that this discrepency between runtime and compile time, at least when both are `is_iec559`, is a bug. Any idea if it's legal for any of `std::numeric_limits`'s values to disagree between runtime and `constexpr` time? – Ben Nov 29 '22 at 20:06