考慮以下代碼:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
long double test = 0xFFFFFFFFFFFFFFFF;
cout << "1: " << test << endl;
unsigned long long test2 = test;
cout << "2: " << test2 << endl;
cout << "3: " << (unsigned long long)test << endl;
return 0;
}
使用 GCC g (7.5.0) 編譯此代碼并運行會按預期產生以下輸出:
1: 1.84467e 19
2: 18446744073709551615
3: 18446744073709551615
但是,使用 Microsoft Visual C 編譯器(16.8.31019.35,64 位和 32 位)編譯并運行會產生以下輸出:
1: 1.84467e 19
2: 9223372036854775808
3: 9223372036854775808
將值轉換為 a 時unsigned long long,MSVC 編譯器不會給出大于 a (signed) 的最大值的值long long。
難道我做錯了什么??
我是否遇到了我不知道的編譯器限制?
有誰知道這個問題的可能解決方法?
uj5u.com熱心網友回復:
因為 MSVClong double實際上只是一個double(正如 @drescherjm 在評論中指出的那樣),它沒有足夠的精度來包含 0xFFFFFFFFFFFFFFFF 的確切值。當此值存盤在 中時,long double它會“四舍五入”為大于 0xFFFFFFFFFFFFFFFF 的值。這會在轉換為unsigned long long.
uj5u.com熱心網友回復:
您看到未定義的行為,因為正如評論中所指出的, a與 MSVClong double中的 a 相同,并且您的(or )double的“轉換”值實際上被“四舍五入”到稍微(但顯著)更大的值,可以在以下代碼中可以看到:0xFFFFFFFFFFFFFFFFULLONG_MAX
int main(int argc, char* argv[])
{
long double test = 0xFFFFFFFFFFFFFFFF;
cout << 0xFFFFFFFFFFFFFFFFuLL << endl;
cout << fixed << setprecision(16) << endl;
cout << test << endl;
return 0;
}
輸出:
18446744073709551615
18446744073709551616.0000000000000000
因此,當將該浮點值轉換回 時,您將違反此 Microsoft 檔案unsigned long long中指定的轉換規則:
- 對于轉換為
unsigned longorunsigned long long,轉換超出范圍的值的結果可能是除了最高或最低可表示值之外的某個值。結果是標記值還是飽和值取決于編譯器選項和目標體系結構。未來的編譯器版本可能會回傳飽和值或標記值。
通過切換到可以在 Visual Studio 中使用的 clang-cl 編譯器,可以進一步“驗證”這個 UB(需要一個更好的術語)。對于您的原始代碼,這將給出0“2”和“3”輸出行上的值。
假設 clang (LLVM) 編譯器不受上述“微軟規則”的約束,我們可以轉而使用 C 標準:
7.10 浮點積分轉換 [conv.fpint]
1 浮點型別的純右值可以轉換為整數型別的純右值。轉換截斷;也就是說,小數部分被丟棄。如果截斷的值不能在目標型別中表示,則行為未定義。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/437754.html
上一篇:你能為一個概念中的函子指定一個回傳型別(尤其是一個void回傳型別)嗎?
下一篇:C 模板lambda包裝器
