簡體   English   中英

為什么代碼片段會拋出浮點數異常?

[英]Why does the snippet throw a Floating point exception?

這個

double what;
for (int i = 1; i < (long)pow(10, 7); i++)
    what = (i + i) / (i * i) - i; 

引發浮點異常(核心轉儲)。 為什么? 我正在使用 clang++。

根據您的平台, int可能是 32 位或 64 位寬。

來自[basic.fundamental]/2[basic.fundamental]/3 [摘錄,強調我的]:

[basic.fundamental]/2

有五種標准的有符號 integer 類型:“ signed char ”、“ short int ”、“ int ”、“ long int ”和“ long long int ”。 在此列表中,每種類型提供的存儲空間至少與列表中它前面的那些一樣多 [...] plain int具有執行環境架構建議的自然大小; 提供其他簽名integer型號以滿足特殊需要。

[basic.fundamental]/3

對於每個標准的有符號 integer 類型,存在一個對應的(但不同的)標准無符號 integer 類型:“ unsigned char ”、“ unsigned short int ”、“ unsigned int ”、“ unsigned long int ”和“ unsigned long long int ” ”,其中每一個都占用相同的存儲量,並且與對應的簽名integer類型具有相同的alignment要求; [...]

有符號和無符號 integer 類型應滿足 C 標准第 5.2.4.2.1 節中給出的約束

我們可以 go 到C11 標准草案[摘錄,強調我的]:

5.2.4.2.1 integer 類型的大小<limits.h>

[...] 它們的實現定義值的大小(絕對值)應等於或大於所示值,且符號相同。

[...]

  • int類型的 object 的最大值: INT_MAX +32767

[...]

如果不知道目標/架構細節,這對我們沒有幫助,但是,為了簡化您的問題,讓我們考慮使用固定寬度帶符號整數的示例,並注意以下示例“很好”(在此上下文中):

#include <cstddef>
#include <math.h>

int main() {    
    double what;
    for (int32_t i = 1; i < (int32_t)pow(10, 4); i++)
    {
        what = (i + i) / (i * i) - i; 
    }
    (void)what;
    return 0;
}

對於我嘗試的特定執行,以下結果導致“浮點異常”(UB;龍可能會飛出我們的鼻子,見下文):

#include <cstddef>
#include <math.h>

int main() {    
    double what;
    for (int32_t i = 1; i < (int32_t)pow(10, 5); i++)
    {
        what = (i + i) / (i * i) - i; 
    }
    (void)what;
    return 0;
}

這里的關鍵是int32_t的最大值是2,147,483,647 ,這意味着i * i將溢出pow(10, 5)的大小值。 簽名 integer 溢出是未定義的行為 (UB),從那里開始一切正常 在這種情況下,UB 很可能巧合地從表達式i * i的溢出中產生一個值0 ,這反過來導致在表達式(i + i) / (i * i)更巧合的是,這很可能是浮點數異常的根本情況。

我在這里強調是巧合,因為超出 UB 的任何點都會成為邏輯分析的無用目標; 編譯器供應商可能會假設我們從來沒有 UB 並且一旦我們進入 UB 域就會做任何事情。 我們看到的任何結果都應該被認為是巧合,除非您在特定目標體系結構和硬件上使用 C++ 的某些非標准方言,例如簽名 integer 溢出等特定情況可能被定義為非標准實現定義(和因此由特定的實現指定)而不是 UB。

你的int溢出是因為 10^14 不適合我見過的任何int (請注意int是平台相關的)。 在某些平台上,10^7 已經溢出。 有符號溢出總是導致未定義的行為,有時會導致0

  • 您可能希望直接使用大小合適的類型(例如std::uint64_tdouble )來存儲循環變量。
  • 您為每個循環迭代計算pow 在循環外計算一次。
  • 您的循環似乎丟棄了除最后一次計算之外的所有內容。 也許你可以計算那個?

for循環中的i < (long)pow(10, 7)條件導致 integer (i * i)表達式變得大於其最大值,從而導致 integer 溢出和未定義的行為 在某些實現中, i可能變為01 ,導致以下表達式: (i / i)變為0 例如,這反過來可能(在 Visual Studio 中)導致在調試期間出現Integer 除零異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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