考慮以下代碼:
#include <math.h>
#include <complex.h>
#include <stdio.h>
int main()
{
complex double A = CMPLX(-NAN, 0.0);
complex double B = CMPLX(NAN, 0.0);
printf("A = %e %e, B = %e %e\n", creal(A), cimag(A), creal(B), cimag(B));
B = 0.5 * A;
printf("A = %e %e, B = %e %e\n", creal(A), cimag(A), creal(B), cimag(B));
}
在具有 gcc 8.5.0、10.2.1 和 gcc 12 的各種 amd64 Debian 系統上,此代碼產生以下輸出:
沒有優化的輸出
# cc -o test test.c -lm && ./test
A = -nan 0.000000e 00, B = nan 0.000000e 00
A = -nan 0.000000e 00, B = nan 0.000000e 00
輸出 -O1
# cc -O1 -o test test.c -lm && ./test
A = -nan 0.000000e 00, B = nan 0.000000e 00
A = -nan 0.000000e 00, B = -nan 0.000000e 00
我不明白為什么輸出不同。在 FreeBSD 上,我無法獲得“-O1”輸出。相反,我使用所有編譯器標志測驗的所有 gcc 版本總是產生“無優化”輸出。
為什么輸出因優化標志而異?這是編譯器錯誤還是未定義的行為?我能做些什么來避免這個問題?
uj5u.com熱心網友回復:
IEEE 754 沒有指定運算式-NAN(或任何涉及 NAN 的任何計算)的符號位。C 對宏的唯一要求NAN是結果是一個常量運算式,其值為安靜的 NaN。據我所知,沒有任何東西可以保證每次呼叫NAN.
根據 IEEE-754,傳播 NaN(如.5*-NAN)的操作需要傳播 NaN 的有效載荷(包括第一位之后的尾數)。但它們不需要傳播符號位;該操作可能會也可能不會遵循符號位的通常約定。
通常,不僅對于 NaN,如果計算有多個有效結果,C 標準不強制編譯器為每個計算選擇相同的有效結果。在某些情況下,可以合法地以超出所使用資料型別的精度計算中間結果,這可能會改變最終結果的舍入值。類似地,如果一個操作可能導致兩個或多個不同的 NaN 位模式,則編譯器沒有義務安排一致。例如,兩個 NaN 的總和是一個 NaN,IEEE-754 的傳播負載要求要求結果的負載是輸入的負載之一。但它沒有指定哪一個,所以如果編譯器碰巧有時計算A B而有時B A,并且那些傳播不同的有效載荷,這是有效的。
特別是,編譯器可以在編譯時自由折疊常量計算,并且編譯時計算不需要與實際計算按位相同,否則編譯器不折疊常量可能會發出。(當然,它們必須是正確的。但有時有不止一個正確的位模式。)我很確定這就是這里發生的事情:GCC 在 -O1 處比在 -O0 處更積極地折疊常量。(與 Clang 進行對比。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/522231.html
上一篇:史密斯數程式永不結束(無限回圈)
下一篇:CS50PSET2可讀性2022
