[英]Are floating point operations resulting in infinity undefined behavior for IEC 559/IEEE 754 floating-point types
我正在閱讀Infinity而不是constexpr ,這似乎表明創建無限是未定義的行為:
如果在評估表達式期間,結果未在數學上定義或未在其類型的可表示值范圍內,則行為未定義。
但是,如果std::numeric_limits::is_iec559
等於true,它似乎給了我們更多的保證。
下面的代碼使用此保證來創建無限數字。 在constexpr
上下文中執行時,會導致編譯器失敗,因為這是undefined behavior
,以防is_iec559
等於false。
// 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");
由於此函數始終導致無限,因此永遠無法在有效的C ++程序中調用它。 但是,正如我們在is_iec559
上斷言的is_iec559
,我們得到了額外的保證。 這個程序仍然無效嗎?
is_iec559
什么is_iec559
? (答案可以使用C ++ 17作為即將推出的C ++ 20,請清楚說明使用哪個)
該計划格式不正確。
Per [expr.const] / 4 ,
表達式
e
是核心常量表達式,除非根據抽象機器的規則評估e
將評估以下表達式之一:
[...]
在本文檔的[intro]到[cpp]中指定的具有未定義行為的操作 [ 注意:包括,例如,有符號整數溢出( [expr.prop] ),某些指針算術( [expr.add] ),除零或某些班次操作 - 結束說明 ];
[...]
如果在評估表達式期間,結果未在數學上定義或未在其類型的可表示值范圍內,則行為未定義。 [ 注意:除零處理,使用零除數形成余數,所有浮點異常因機器而異,有時可通過庫函數調整。 - 結束說明 ]
請注意,從數學上講,兩個有限數的乘積永遠不會無窮大,因此產品會觸發未定義的行為。 實現可以指定已定義的表達式,但從標准的角度來看仍然是未定義的行為。 實施的定義不追溯適用於標准的其他部分。 因此,程序格式錯誤,因為它試圖將觸發未定義行為的表達式作為常量表達式進行求值。
有趣的是, numeric_limits<double>::infinity()
是constexpr
。 這可以。 每[numeric.limits] /無窮大 :
static constexpr T infinity() noexcept;
如果可用,表示正無窮大。
對
has_infinity != false
所有特化都有has_infinity != false
。 在is_iec559 != false
專業化中必需。
如果is_iec559 == true
,則has_infinity == true
,並返回無窮大值。 如果is_iec559 == false
,則has_infinity
可能為true
,在這種情況下也會返回無窮大值,或者它可能為false
,在這種情況下, infinity()
返回0
。 (!)
但是,由於兩個大數的乘積不是自動無窮大(相反,它是未定義的行為),所以沒有矛盾。 傳遞無窮大很好(無窮大值總是在可表示值的范圍內),但是乘以兩個大數並假設結果是無窮大則不是。
等待一段時間有時會有所幫助,看起來Clang已收到一個補丁,使這段代碼編譯: https : //reviews.llvm.org/D63793
在r329065之前,我們使用[-max,max]作為可表示值的范圍,因為LLVM的fptrunc在從較大的浮點類型截斷到較小的浮點類型時不保證定義的行為。 現在已經修復了,我們可以使clang在這方面遵循正常的IEEE 754語義,並將更大的范圍[-inf,+ inf]作為可表示值的范圍。
注釋的有趣元素(該修訂中的代碼注釋的一部分)是(還)允許導致NaN的操作:
// [expr.pre]p4:
// If during the evaluation of an expression, the result is not
// mathematically defined [...], the behavior is undefined.
// FIXME: C++ rules require us to not conform to IEEE 754 here.
#include <limits>
constexpr double createNan()
{
static_assert(std::numeric_limits<double>::is_iec559, "asdf");
double d = std::numeric_limits<double>::infinity() / std::numeric_limits<double>::infinity();
return -1*d;
}
static_assert(createNan() != 0., "NaN");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.