簡體   English   中英

浮點運算是否導致IEC 559 / IEEE 754浮點類型的無限未定義行為

[英]Are floating point operations resulting in infinity undefined behavior for IEC 559/IEEE 754 floating-point types

我正在閱讀Infinity而不是constexpr ,這似乎表明創建無限是未定義的行為:

[expr] / 4

如果在評估表達式期間,結果未在數學上定義或未在其類型的可表示值范圍內,則行為未定義。

但是,如果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
  • 如果有效? 為什么它在運行時有效而不在constexpr上下文中?

(答案可以使用C ++ 17作為即將推出的C ++ 20,請清楚說明使用哪個)

該計划格式不正確。

Per [expr.const] / 4

表達式e是核心常量表達式,除非根據抽象機器的規則評估e將評估以下表達式之一:

  • [...]

  • 在本文檔的[intro][cpp]中指定的具有未定義行為的操作 [ 注意:包括,例如,有符號整數溢出( [expr.prop] ),某些指針算術( [expr.add] ),除零或某些班次操作 - 結束說明 ];

  • [...]

[expr.pre] / 4說:

如果在評估表達式期間,結果未在數學上定義或未在其類型的可表示值范圍內,則行為未定義。 [ 注意:除零處理,使用零除數形成余數,所有浮點異常因機器而異,有時可通過庫函數調整。 - 結束說明 ]

請注意,從數學上講,兩個有限數的乘積永遠不會無窮大,因此產品會觸發未定義的行為。 實現可以指定已定義的表達式,但從標准的角度來看仍然是未定義的行為。 實施的定義不追溯適用於標准的其他部分。 因此,程序格式錯誤,因為它試圖將觸發未定義行為的表達式作為常量表達式進行求值。


有趣的是, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM