Re: [問題] float (加減乘除) int 問題

看板C_and_CPP (C/C++)作者 (remizu)時間12年前 (2014/04/22 17:44), 12年前編輯推噓1(101)
留言2則, 1人參與, 最新討論串2/2 (看更多)
剛好這個問題小弟覺得小弟有辦法回答, 趁本板高手們還沒出沒之前回一下, 希望能夠拋磚引玉, 若觀念有誤還請前輩們多多指教。 ※ 引述《SuperMaster (神手)》之銘言: : 小弟我再進行C++測試時遇到一點小問題 : [問題1:Type checking概念] : 測試1: : int a 10 ; : float b = 2.5 ; : b = a + b ; : printf( "%f", b ) ; : 印出值為:12.5 << 因為會發生型別轉換 : 測試2: : int a = 10 ; : float b = 2.5 ; : b = a / 3 ; : printf( "%f", b ) ; : 印出值為:3 << 沒有轉換 應該要是3.33333 : 需要改成b = (float)a / 3 才能印出正確的值 : 為什麼測試1的不需要就能印出正確的值?????? 這是個arithmetic expression type conversion的問題, 用簡單的一句話回答就是:C++標準這樣規定。 對於built-in的arithmetic type來說, 當operator兩邊的operands型別不同時, compiler會自動發生一些型別轉換。 原原PO的測試1中,a是int型別,b是float型別。 當執行a + b時,a會被轉換成float型別, 然後再進行float加法動作,最後將其結果賦值給b。 所以能得到原原PO預期中的正確結果。 在測試2中,由於變數a及字面常數3都是int型別。 因此a / 3實際上是int / int的操作。 執行的是所謂的整數除法, 根據標準,其結果的小數部分將被直接捨去。 10 / 3 = 3.333...捨去小數部分後得到3, 最後將這個int型別的3轉型成float並賦值給b。 若想獲得3.333...的結果,可以如前篇推文所說, 在算術表達式中進行顯式轉型: b = static_cast<float>(a) / 3; // Cast variable 'a' to float type. 或 b = a / 3.0f; // Let literal constant be float type. 從而令算術表達式進行時, 另一個operand也能夠被隱式轉型為float。 關於int / int的整數除法行為, 與float / float的浮點數除法行為不同之處。(前者捨去小數部分) 則可以視為是一種built-in的operator overloading。 雖然,在算術表達式中,看起來總是由小型別轉換至大型別。 但實際上並非總是如此, 這些算術表達式型別轉換規則比想像的還要更複雜一些, 以C++11的標準來說, 大致上可以依循下列規則確定operand會被轉換成什麼型別: 1. 若operator兩邊有其中一邊的operand是long double型別, 則另一邊的operand也會被轉換成long double型別。 2. 否則,若其中一邊的operand是double型別, 則另一邊的operand也會被轉換成double型別。 3. 否則,若其中一邊的operand是float型別, 則另一邊的operand也會被轉換成float型別。 // 原原PO問題即適用這條 4. 否則,若兩邊的operand都是整數型別, 則會進行一個叫integral promotion(*1)的處理。 5. 在這種情況下,若兩邊的operand皆為signed或皆為unsigned, 則級別較低的型別會被轉型為級別較高的型別。 6. 若不符合上述規則,則表示一邊為signed、一邊為unsigned, 若unsigned的operand型別級別較高, 則另一邊的operand將被轉換為unsigned operand的型別。 7. 否則,當signed operand的型別可容納unsigned operand型別 所能表示的任意值時,unsigned operand將被轉型成signed operand的型別。 8. 當上述情況皆不符合時,兩邊的operands都會被轉換成 signed operand的型別的unsigned版本。 *1. 什麼是integral promotion? 當compiler在評估算術表達式時,會將bool、char、unsigned char、 signed char以及short自動轉換為int,並將bool值true轉換為1, false轉換為0,這個過程就叫做integral promotion。舉例來說: short power = 100; short defense = 60; short hp = power - defense; // here 此時,power及defense皆會被轉型成int,運算結果是 int型別的40,然後再轉型回short並賦值給hp。做這種轉換通常是為了 能夠更有效率地做運算,詳細就不再贅述。 下面舉一些例子: long double llfResult; double lfResult; float fResult; long long int llResult; unsigned long ulResult; int iResult; short nResult; long double llfValue = 789.12L; double lfValue = 456.78; float fValue = 123.45f; long long llValue = 99999999LL; unsigned long ulValue = 98765L; int iValue = 100; unsigned int uValue = 120u; short nValue = 75; char chValue = 13; // According to rule 1, fValue will be converted to long double. llfResult = llfValue + fValue; // According to rule 2, fValue will be converted to double. lfResult = lfValue + fValue; // According to rule 3, iValue will be converted to float. fResult = fValue + iValue; // According to rule 4, nValue will be converted to int. iResult = iValue + nValue; // According to rule 4, both chValue and nValue will be converted // to int and perform addition operation, and then convert back to // short, assign to nResult. nResult = chValue + nValue; // According to rule 6, iValue will be converted to unsigned long. ulResult = ulValue + iValue; // According to rule 7, uValue will be converted to long long. llResult = llValue + uValue; 要留心的是,上述規則為C++11的標準,與C++03以及ANSI C標準有些微差異, 詳細狀況還得看自己的編譯環境而定。 : ----------------------------------------------------- : [問題2:宣告] : (恕刪) -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 59.115.6.1 ※ 文章網址: http://www.ptt.cc/bbs/C_and_CPP/M.1398159848.A.C34.html

04/22 21:50, , 1F
我記得在 function overloading,promotion會比標準轉換優先
04/22 21:50, 1F

04/22 21:50, , 2F
這是其中之一的差異
04/22 21:50, 2F
感謝補充。 在這裡再補述一下, 在尋找適合的function overloads時,會依照 1. 參數完全符合 2. Type Promotion 3. 隱式標準轉換 4. 使用者自訂轉換及建構式 的順序進行尋找。 例如: void foo(int); // function 1 void foo(short); // function 2 int main(void) { bool b = true; char ch = 'a'; short n = 123; int i = 12345; foo(b); // call function 1, type is promoted to int. foo(ch); // call function 1, type is promoted to int. foo(n); // call function 2, exact match. foo(i); // call function 1, exact match. return 0; } ※ 編輯: remizu (59.115.30.23), 04/24/2014 00:57:26
文章代碼(AID): #1JLZdemq (C_and_CPP)
文章代碼(AID): #1JLZdemq (C_and_CPP)