【问题标题】:1.0 / 0.0 - valid statement?1.0 / 0.0 - 有效的声明?
【发布时间】:2021-12-29 20:25:33
【问题描述】:

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

【问题讨论】:

  • 所以我使用了 1.0f / 0.0f 的负载因子,因为它比 numeric_limits 更短::infinity() 这样是否明智?
  • msvc报什么错误?
  • @HolyBlackCat 可复制:godbolt,错误为error C2124: divide or mod by zero
  • 这能回答你的问题吗? stackoverflow.com/q/51474239/4117728。请注意关于 IEEE 与标准 C++ 的讨论以及热门答案之间的差异

标签: c++ ieee-754


【解决方案1】:

C++20 标准

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

7.6.5 Multiplicative operators(强调我的)

(4) 二进制/ 运算符产生商,二进制% 运算符产生第一个表达式除以第二个表达式的余数。 如果/% 的第二个操作数为零,则行为未定义。 对于整数操作数,/ 运算符产生代数商,并丢弃任何小数部分;如果商a/b 可以在结果类型中表示,则(a/b)*b + a%b 等于a;否则,a/ba%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;
}
  • clang & icc:没有警告/错误,将假定1.0 / 0.0 是无穷大,可以进行常量折叠
  • gcc:没有警告/错误,但不会对其进行持续折叠(因此除法 1.0 / 0.0 将在运行时发生!)
  • msvc:产生错误:error C2124: divide or mod by zero

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

std::numeric_limits&lt;float&gt;::infinity() 相比,这 4 个都可以正常工作。


结论

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

如果你想缩短它,你可以很容易地做到这一点,例如:

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

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

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

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

或类似的东西。

【讨论】:

  • std::numeric_limits&lt;T&gt;::infinity() 仅对浮点类型有意义。对于标准整数类型(charint 等),numeric_limits 的特化使成员 has_infinity 给出 false 值,infinity() 给出零值。对于浮点类型,has_infinity 为真且infinity() 仅在 is_iec559 为真时才有意义(本质上 - 表示 IEEE 浮点表示)。
  • 我没有问这是否是UB,但如果这会导致错误。并且 MSVC 应该支持 1.0 / 0.0 因为 numeric_limits::is_iec559 是真的。
  • @BonitaMontero 编译器可以随意处理 UB,但它们可能会给出错误并拒绝编译,它们可能会产生正确的结果,也可能会产生错误的结果。如果您的程序包含 UB,则基本上任何事情都可能发生。即使numeric_limits&lt;double&gt;::is_iec559 == true,这仍然不会改变它被 UB 除以 0 的事实:1.0 / 0.0
  • 参见3.64 undefined behavior:允许的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以记录的环境特征的方式表现(无论是否发出诊断消息),终止翻译或执行(发出诊断消息)。 - 所以所有编译器都是对的,因为它是 UB。
  • 我的看法不同。如果实现保证符合 IEEE-754,则 1.0 / 0.0 == numeric_limits ::infinity ().
【解决方案2】:

有一个很好的解决方法:1.0 / []() constexpr { return 0.0; }()

【讨论】:

    猜你喜欢
    • 2019-08-04
    • 2017-12-18
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多