簡體   English   中英

為什么編譯器不將浮點 *2 優化為指數增量?

[英]Why doesn't a compiler optimize floating-point *2 into an exponent increment?

我經常注意到 gcc 將乘法轉換為可執行文件中的移位。 intfloat相乘時可能會發生類似的事情。 例如, 2 * f可能只是將f的指數增加 1,從而節省一些周期。 編譯器,也許如果有人要求他們這樣做(例如通過-ffast-math ),通常會這樣做嗎?

編譯器通常是否足夠聰明來執行此操作,還是我需要使用scalb*()ldexp()/frexp()函數系列自己執行此操作?

例如,2 * f 可能只是將 f 的指數增加 1,從而節省一些周期。

這根本不是真的。

首先,您有太多的極端情況,例如零、無窮大、Nan 和非正規數。 然后你有性能問題。

誤解是增加指數並不比做乘法快。

如果您查看硬件說明,則沒有直接增加指數的方法。 所以你需要做的是:

  1. 按位轉換為整數。
  2. 增加指數。
  3. 按位轉換回浮點數。

在整數和浮點執行單元之間移動數據通常存在中等到大的延遲。 所以最終,這種“優化”變得比簡單的浮點乘法更糟糕。

所以編譯器不做這種“優化”的原因是因為它並沒有更快。

在現代CPU 上,乘法通常具有每周期一個的吞吐量和低延遲。 如果該值已經在一個浮點寄存器中,你就無法通過在表示上進行整數運算來擊敗它。 如果它開始在內存中,並且您假設當前值和正確結果都不是零、非正規、nan 或無窮大,那么執行類似的操作可能會更快

addl $0x100000, 4(%eax)   # x86 asm example

乘以二; 我唯一能看到這有好處的時候是,如果您正在處理一個從零到無窮大有界的浮點數據的整個數組,並且以 2 的冪進行縮放是您將要執行的唯一操作(所以您沒有任何現有理由將數據加載到浮點寄存器中)。

常見的浮點格式,尤其是 IEEE 754,不會將指數存儲為簡單整數,將其視為整數將不會產生正確的結果。

在 32 位浮點數或 64 位雙精度數中,指數字段分別為 8 位或 11 位。 指數代碼 1 到 254(浮點數)或 1 到 2046(雙精度數)確實像整數一樣:如果將這些值之一加 1,結果是這些值之一,則表示的值會加倍。 但是,在這些情況下添加一個失敗:

  • 初始值為 0 或低於正常值。 在這種情況下,指數字段從零開始,向其加 1 將 2 -126 (浮點數)或 2 -1022 (雙精度數)加到數字上; 它不會使數字翻倍。
  • 初始值超過 2 127 (浮點數)或 2 1023 (雙精度數)。 在這種情況下,指數字段從 254 或 2046 開始,向其加 1 會將數字更改為 NaN; 它不會使數字翻倍。
  • 初始值是無窮大或 NaN。 在這種情況下,指數字段從 255 或 2047 開始,向其添加 1 會將其更改為零(並且可能溢出到符號位)。 結果為零或次正常,但應分別為無窮大或 NaN。

(以上為正號,情況與負號對稱。)

正如其他人所指出的,一些處理器沒有用於快速處理浮點值位的設施。 即使在那些這樣做的情況下,指數字段也沒有與其他位隔離,因此在上面的最后一種情況下,您通常無法在不溢出到符號位的情況下向其添加一個。

盡管某些應用程序可以容忍諸如忽略次正規數或 NaN 甚至無窮大之類的快捷方式,但應用程序很少會忽略零。 由於向指數添加 1 無法正確處理零,因此無法使用。

這與編譯器或編譯器編寫者不聰明無關。 這更像是遵守標准並產生所有必要的“副作用”,例如 Infs、Nans 和 denormals。

也可能是產生其他不需要的副作用,例如讀取記憶。 但我確實認識到在某些情況下它可以更快。

實際上,這就是硬件中發生的事情。

2也作為浮點數傳入 FPU,尾數為 1.0,指數為 2^1。 對於乘法,指數相加,尾數相乘。

鑒於有專用硬件來處理復雜情況(乘以不是 2 的冪的值),並且特殊情況的處理不會比使用專用硬件更糟糕,因此擁有額外的電路和指令是沒有意義的.

這是我在 GCC 10 中看到的實際編譯器優化:

x = 2.0 * hi * lo;

生成此代碼:

mulsd   %xmm1, %xmm0      # x = hi * lo;
addsd   %xmm0, %xmm0      # x += x;

對於嵌入式系統編譯器來說,具有特殊的按 2 次冪縮放的偽操作可能很有用,它可以由代碼生成器以對所討論的機器最佳的任何方式進行轉換,因為在某些嵌入式處理器上,專注於指數可能比執行完整的 2 次冪乘法快一個數量級,但在乘法最慢的嵌入式微機上,編譯器可能會通過讓浮點乘法例程檢查其參數來實現更大的性能提升在運行時跳過尾數為零的部分。

關於乘以 2 的冪的先前 Stackoverflow 問題 共識和實際實現證明,不幸的是,目前沒有比標准乘法更有效的方法。

如果您認為乘以 2 意味着將指數增加 1,請再想一想。 以下是 IEEE 754 浮點運算的可能情況:

情況 1:Infinity 和 NaN 保持不變。

情況 2:通過增加指數並將尾數(符號位除外)設置為零,將具有最大可能指數的浮點數更改為無窮大。

情況 3:指數小於最大可能指數的標准化浮點數的指數增加 1。 伊皮!!!

情況 4:設置了最高尾數位的非規范化浮點數的指數增加 1,將它們變成規范化數字。

情況 5: 清零最高尾數位(包括 +0 和 -0)的非規范化浮點數的尾數向左移動一位位置,保持指數不變。

我非常懷疑生成整數代碼正確處理所有這些情況的編譯器是否會像處理器內置的浮點一樣快。 而且只適合乘以2.0。 對於乘以 4.0 或 0.5,適用一套全新的規則。 對於乘以 2.0 的情況,您可能會嘗試用 x + x 替換 x * 2.0,許多編譯器都這樣做。 那就是他們這樣做,因為處理器可能能夠例如同時進行一次加法和一次乘法,但不能同時進行一次。 所以有時你更喜歡 x * 2.0,有時更喜歡 x + x,這取決於其他操作需要同時做什么。

暫無
暫無

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

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