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 ?
CodePudding user response:
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 quotienta/b
is representable in the type of the result,(a/b)*b a%b
is equal toa
; otherwise, the behavior of botha/b
anda%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.
CodePudding user response:
There's a nice workaround: 1.0 / []() constexpr { return 0.0; }()