-2

I just wanted to apply an infinity load-factor to a std::set<> because I wanted to have a fixed number of buckets. So I used a load-factor of 1.0f / 0.0f because it's shorter to write than numeric_limits<float>::infinity(). MSVC give an error because of the division by zero. clang-cl and clang++ compiled the code without errors. So which compiler is right ?

Bonita Montero
  • 2,817
  • 9
  • 22

2 Answers2

9

C++20 Standard

According to the C++ standard division / modulo with the second operand being 0 is always undefined behavior, both for integral & floating point types:

7.6.5 Multiplicative operators (emphasis mine)

(4) The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. For integral operands the / operator yields the algebraic quotient with any fractional part discarded; if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a; otherwise, the behavior of both a/b and a%b is undefined.

So from a pure standard perspective dividing by 0 is always undefined behavior.


What the compilers do

Given that it's undefined behavior you can't rely on any compiler to produce a consistent result for this - but nonetheless here's what currently happens:

Sample program: godbolt

#include <limits>

int main() {
    float f = 1.0f / 0.0f;
    //float f = std::numeric_limits<float>::infinity();
    if(f == std::numeric_limits<float>::infinity())
        return 123;
    else
        return 345;
}
  • clang & icc: no warnings / errors, will assume 1.0 / 0.0 is infinity, can do constant folding
  • gcc: no warnings / errors, but won't do constant folding for it (so the division 1.0 / 0.0 will happen at runtime!)
  • msvc: produces an error: error C2124: divide or mod by zero

So 3 different results, depending on which compiler you're using.

In comparison to std::numeric_limits<float>::infinity(), which will work correctly for all 4 of them.


Conclusion

Always use std::numeric_limits<float>::infinity(), since dividing by zero is always undefined behavior.

If you want to shorten it you can easily do so, e.g.:

template<std::floating_point T>
constexpr T Inf = std::numeric_limits<T>::infinity();

// usage:
float f = Inf<float>;
double d = Inf<double>;

or

constexpr float fInf = std::numeric_limits<float>::infinity();
constexpr double dInf = std::numeric_limits<double>::infinity();

// usage:
float f = fInf;
double d = dInf;

or something similar.

Turtlefight
  • 9,420
  • 2
  • 23
  • 40
  • 1
    `std::numeric_limits::infinity()` is only meaningful for floating point types. For standard integral types (`char`, `int`, etc) the specialisations of `numeric_limits` have the member `has_infinity` give a `false` value, and `infinity()` give a zero value. For floating point types, `has_infinity` is true AND `infinity()` is meaningful only if `is_iec559` is true (essentially - indicating an IEEE floating point representation). – Peter Dec 30 '21 at 02:13
  • I didn't asked if this is UB, but if this should result in an error. And MSVC should support 1.0 / 0.0 because numeric_limits::is_iec559 is true. – Bonita Montero Dec 30 '21 at 07:03
  • @BonitaMontero compilers are free to handle UB however they like - they might give you an error and refuse to compile, they might produce the correct result, the might produce the wrong result. If your program contains UB basically anything can happen. Even if `numeric_limits::is_iec559 == true`, that still doesn't change the fact that it's UB to divide by 0: `1.0 / 0.0`. – Turtlefight Dec 30 '21 at 07:09
  • see [3.64 undefined behavior](https://eel.is/c++draft/intro.defs#defns.undefined): Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). - So all compilers are right because it's UB. – Turtlefight Dec 30 '21 at 07:12
  • I see it differently. If the implementation guarantees IEEE-754 conformity, then 1.0 / 0.0 == numeric_limits ::infinity (). – Bonita Montero Dec 30 '21 at 10:15
  • @BonitaMontero that would be the **to behaving during translation or program execution in a documented manner characteristic of the environment** part - it **can** work if the compiler documents it. But this is ultimately up to the compiler what action it wants to take - all of the above interpretations are valid from a c++ standard perspective. – Turtlefight Dec 30 '21 at 10:20
  • No, the compiler can't provide a different behaviour if numeric_limits::is_iec559 == true, i.e. the implementation says that it is IEEE-754 conformant.. IEEE-754 requires 1.0 / 0.0 == +Inf. – Bonita Montero Dec 30 '21 at 10:31
-2

There's a nice workaround: 1.0 / []() constexpr { return 0.0; }()

Bonita Montero
  • 2,817
  • 9
  • 22