C++:标准保证哪些浮点运算(如果有)不会抛出异常?

Kri*_*ege 1 c++ floating-point exception nothrow c++20

在 C++20(或更高版本)中,是否有任何涉及浮点数的操作由标准保证永远不会抛出?

关于什么

  1. 赋值和复制构造?
  2. 比较?
  3. 算术?

注意,我在这里只关心普通的常规 C++ 异常,而不是“浮点异常”的特殊概念。

这是我的问题的更精确的表述:

如果ab是 类型的可变对象double,则 C++ 标准保证以下哪些表达式(如果有)计算结果为 true?

  1. noexcept(a = b)
  2. noexcept(a == b)
  3. noexcept(a + b)

对于新读者,我想澄清一下,我只对“定义的行为”感兴趣,即所有不是未定义行为的东西。

Jan*_*tke 5

需要注意的是,浮点异常指的是诸如FE_DIVBYZEROFE_INEXACT中定义的异常<cfenv>。从某种意义上说,这些不是例外std::exception,而是浮点环境中的标志,可以使用 进行检查std::fexceptflag

\n

未定义行为可能会引发异常

\n

话虽这么说,浮点运算原则上会抛出常规异常:

\n
\n

如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。

\n

[:\xe2\x80\x82除以零的处理,使用零除数形成余数,并且所有浮点异常因机器而异,有时可以通过库函数调整。\n\xe2\x80\x94 end笔记]

\n
\n

- [expr.pre] p4

\n

当浮点运算产生未定义的结果时,本段可能会起作用。\n除以零通常是 C++ 中的未定义行为,浮点数也不例外,即使std::numeric_limits<float>::is_iec559true

\n

执行除以零时,实现可以选择抛出异常,因为除以零是 UB,因此实现可以做任何它想做的事情。\n这就是函数具有狭窄契约的原因之一(请参阅Lakos 规则)没有被标记noexcept,即使是那些来自 C 标准库的,否则不能抛出(参见[res.on.exception.handling] p2)。

\n

noexept(...)true浮点运算

\n
\n
    \n
  1. noexcept(a = b)
  2. \n
  3. noexcept(a == b)
  4. \n
  5. noexcept(a + b)
  6. \n
\n
\n

此外,所有这些都可以保证,true因为操作员只考虑是否可能引发noexcept异常。它不考虑是否会因未定义的行为而引发异常。

\n

相等比较(参见[expr.eq])和赋值(参见[expr.ass])永远不会是 UB,因此它们甚至不能通过这种方式抛出异常(除非使用未初始化的值)。参见[except.throw] 注 1有关正常情况下可以抛出异常的表达式列表,

\n

结论

\n

浮点异常与常规异常不同;他们只有名字相同。理论上当浮点运算运行到 UB 中时,可能会抛出传统异常,尽管实际上没有编译器这样做

\n

  • @JanSchultke 注意这个脚注:184)也就是说,C 库函数都可以被视为标记为 noexcept。这允许实现根据运行时不存在异常来进行性能优化 (2认同)