1.0 / 0.0 - 有效的声明?

Bon*_*ero -2 c++ ieee-754

我只是想对 std::set<> 应用无穷大的加载因子,因为我想要有固定数量的存储桶。所以我使用了 1.0f / 0.0f 的负载因子,因为它比 numeric_limits<float>::infinity() 更短。MSVC 由于被零除而给出错误。clang-cl 和 clang++ 编译代码没有错误。那么哪个编译器是正确的呢?

Tur*_*ght 9

C++20 标准

根据 C++ 标准,第二个操作数为 0 的除法/取模始终是未定义的行为,无论是整型还是浮点类型:

7.6.5 乘法运算符(重点是我的)

(4)二元/运算符产生商,二元运算%符产生第一个表达式除以第二个表达式的余数。如果or的第二个操作数为零,则行为未定义。/%对于整数操作数,/运算符产生代数商,并丢弃任何小数部分;如果商a/b可以用结果的类型表示,(a/b)*b + a%b则等于aa/b否则,和的行为a%b都是未定义的。

因此,从纯粹的标准角度来看,除以 0 始终是未定义的行为。


编译器做什么

鉴于这是未定义的行为,您不能依赖任何编译器来为此生成一致的结果 - 但尽管如此,当前发生的情况如下:

示例程序: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;
}
Run Code Online (Sandbox Code Playgroud)
  • clang & icc:没有警告/错误,将假设1.0 / 0.0为无穷大,可以进行恒定折叠
  • gcc:没有警告/错误,但不会对其进行持续折叠(因此除法1.0 / 0.0将在运行时发生!)
  • msvc:产生错误:error C2124: divide or mod by zero

所以有 3 种不同的结果,具体取决于您使用的编译器。

与 相比std::numeric_limits<float>::infinity(),这对所有 4 个都可以正常工作。


结论

始终使用std::numeric_limits<float>::infinity(),因为除以零始终是未定义的行为。

如果你想缩短它,你可以轻松地做到这一点,例如:

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

// usage:
float f = Inf<float>;
double d = Inf<double>;
Run Code Online (Sandbox Code Playgroud)

或者

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

// usage:
float f = fInf;
double d = dInf;
Run Code Online (Sandbox Code Playgroud)

或类似的东西。