JVA*_*pen 10 c++ floating-point language-lawyer c++17 c++20
我正在阅读Infinity而不是constexpr,这似乎表明创建Infinity是未定义的行为:
如果在表达式的求值过程中,未在数学上定义结果或该类型的结果不在可表示值的范围内,则行为不确定。
但是,如果std::numeric_limits::is_iec559 等于true,则似乎为我们提供了更多保证。
下面的代码利用此保证来创建无限数。在constexpr上下文中执行时,会导致编译器失败,因为undefined behavior如果情况为is_iec559假,则失败。
// clang++ -std=c++17 -O3
#include <limits>
constexpr double createInfinity()
{
static_assert(std::numeric_limits<double>::is_iec559, "asdf");
double d = 999999999999;
while (d != std::numeric_limits<double>::infinity())
{
d *= d;
}
return -1*d;
}
static_assert(createInfinity() == std::numeric_limits<double>::infinity(), "inf");
Run Code Online (Sandbox Code Playgroud)
由于此函数总是导致无限,因此永远不能在有效的C ++程序中调用它。但是,正如我们对所断言的那样is_iec559,我们获得了额外的保证。该程序仍然无效吗?
is_iec559?(答案可以同时使用C ++ 17和即将推出的C ++ 20,请清楚说明使用的是哪种)
该程序格式不正确。
\n\n\n\n\n\n\n\n\n表达式
\n\ne是核心常量表达式e,除非 的计算遵循抽象机的规则,将计算以下表达式之一:\n
\n- \n
[...]
- \n
具有未定义行为的操作,如 本 文档[ \xe2\x80\x89注意: \n 包括有符号整数溢出\n ( [expr.prop ])、某些指针算术([expr.add])、除以零或某些移位操作 —\xe2\x80\x89尾注\xe2\x80\x89];
- \n
[...]
\n\n\n如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。[\xe2\x80\x89注意:除以零、使用零除数形成余数的处理,以及所有浮点异常的处理因机器而异,有时可由库函数调整。—\xe2\x80\x89尾注\xe2\x80\x89]
\n
请注意,从数学上讲,两个有限数的乘积永远不会无穷大,因此该乘积会触发未定义的行为。实现可以指定已定义的此类表达式,但从标准的角度来看,它仍然是未定义的行为。实现中的定义并不追溯适用于标准的其他部分。因此,该程序是格式错误的,因为它尝试将触发未定义行为的表达式计算为常量表达式。
\n\n不过有趣的是,那numeric_limits<double>::infinity()就是constexpr。这可以。根据[numeric.limits]/infinity:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n\nstatic constexpr T infinity() noexcept;\n正无穷大的表示(如果有)。
\n\n对于
\nhas_\xc2\xadinfinity != false.\n 的专业化是必需的is_\xc2\xadiec559 != false。
如果is_iec559 == true,则has_infinity == true,并且返回无穷大值。如果is_iec559 == false,has_infinity则可能是true,在这种情况下也返回无穷大值,或者也可能是false,在这种情况下infinity()返回0。(!)
然而,由于两个大数的乘积不会自动无穷大(而是未定义的行为),因此不存在矛盾。传递无穷大是可以的(无穷大值始终在可表示值的范围内),但将两个大数相乘并假设结果为无穷大则不然。
\n