今天一打開博客,看到左上角的園齡5年,目光有些恍然,昔日作為學生上課的情景、已經慢慢變的模糊。是啊、畢業已經3年有余,時光不再來...
一、原碼和補碼
在步入正文說類型轉換之前,先做一個小鋪墊,了解一下原碼和補碼。
[注:由于同一個數字在用不同位數的原碼或補碼表示時、結果不同,所以如無特殊說明、該小節下出現的所有原碼和補碼均為8位]
原碼(true form)是一種計算機中對數字的二進制定點表示方法。原碼表示法在數值前面增加了一位符號位(即最高位為符號位):
正數該位為0,負數該位為1(0有兩種表示:+0和-0),其余位表示數值的大小。
1、原碼優點:簡單直觀;例如,我們用8位二進制表示一個數,+11的原碼為00001011,-11的原碼就是10001011
2、原碼缺點:原碼不能直接參加運算,可能會出錯。例如數學上,1+(-1)=0,而在二進制中 00000001+10000001=
10000010,換算成十進制為-2。顯然出錯了。
補碼(two's complement) 在計算機系統中,數值一律用補碼來表示和存儲。補碼是可以直接參與運算的。原碼和補碼表示方法均有
符號位和數值位兩部分,符號位都是用0表示“正”,用1表示“負”,而數值位表示方法不相。
1、知原碼求補碼
求正數的補碼:正整數的補碼與原碼相同。
【例】+9的補碼是00001001。
求負數的補碼:求負整數的補碼, 在原碼的基礎之上 符號位不變,數值位各位取反,最后整個數加1。
【例】求-5的補碼。
-5的原碼(10000101)→符號位不變(10000101)→數值位取反(11111010)→加1(11111011)
所以-5的補碼是11111011。
【例】數0的補碼表示是唯一的。
[+0]補=[+0]原=00000000
[-0]補=11111111+1=00000000
2、知補碼求原碼
已知一個數的補碼,求原碼的操作其實就是對該補碼再求補碼:
1)如果補碼的符號位為“0”,表示是一個正數,其原碼就是補碼。
2)如果補碼的符號位為“1”,表示是一個負數,那么求給定的這個補碼的補碼就是要求的原碼。
【例】已知一個補碼為11111001,則原碼是10000111(-7)。
因為符號位為“1”,表示是一個負數,所以該位不變,仍為“1”。
其余七位1111001取反后為0000110;
再加1,所以是10000111。
3、補碼的運算
補碼:http://baike.baidu.com/view/377340.htm
二、整形間的類型轉換
class PRogram { static void Main(string[] args) { Console.WriteLine("注:該demo中的所有例子均為整形間的無精度損失的類型轉換,為了方便閱讀打印的是字節而不是位"); Console.WriteLine("1、補位式轉化"); Console.WriteLine("/r/n###32位有符號 轉 64位無符號###"); Console.WriteLine("/r/n例1"); int i = int.MaxValue; ulong ul = (ulong)i; Print(BitConverter.GetBytes(i), i.ToString(), "轉化前"); Print(BitConverter.GetBytes(ul), ul.ToString(), "轉化后"); Console.WriteLine("/r/n例2"); i = int.MinValue; ul = (ulong)i; Print(BitConverter.GetBytes(i), i.ToString(), "轉化前"); Print(BitConverter.GetBytes(ul), ul.ToString(), "轉化后"); Console.WriteLine("/r/n###32位有符號 轉 64位有符號###"); Console.WriteLine("/r/n例3"); i = int.MinValue; long l = (long)i; Print(BitConverter.GetBytes(i), i.ToString(), "轉化前"); Print(BitConverter.GetBytes(l), l.ToString(), "轉化后"); Console.WriteLine("/r/n###32位無符號 轉 64位有符號###"); Console.WriteLine("/r/n例4"); uint ui = uint.MaxValue; l = (long)ui; Print(BitConverter.GetBytes(ui), ui.ToString(), "轉化前"); Print(BitConverter.GetBytes(l), l.ToString(), "轉化后"); Console.WriteLine("2、截位式轉化"); Console.WriteLine("/r/n###64位有符號 轉 32位有符號###"); Console.WriteLine("/r/n例5"); l = long.MaxValue; i = (int)l; Print(BitConverter.GetBytes(l), l.ToString(), "轉化前"); Print(BitConverter.GetBytes(i), i.ToString(), "轉化后"); Console.WriteLine("/r/n###64位無符號 轉 32位有符號###"); Console.WriteLine("/r/n例6"); ul = ulong.MaxValue; i = (int)ul; Print(BitConverter.GetBytes(ul), ul.ToString(), "轉化前"); Print(BitConverter.GetBytes(i), i.ToString(), "轉化后"); Console.WriteLine("3、轉換符號位式轉化"); Console.WriteLine("/r/n###32位無符號 轉 32位有符號###"); Console.WriteLine("/r/n例7"); ui = uint.MaxValue; i = (int)ui; Print(BitConverter.GetBytes(ui), ui.ToString(), "轉化前"); Print(BitConverter.GetBytes(i), i.ToString(), "轉化后"); Console.WriteLine("/r/n###32位有符號 轉 32位無符號###"); Console.WriteLine("/r/n例8"); i = int.MinValue; ui = (uint)i; Print(BitConverter.GetBytes(i), i.ToString(), "轉化前"); Print(BitConverter.GetBytes(ui), ui.ToString(), "轉化后"); Console.Read(); } private static void Print(byte[] buffer, string result, string tag = "") { Console.WriteLine(tag); Console.WriteLine("數值:" + result); Console.Write("字節數組(補碼):"); foreach (byte b in buffer) { Console.Write(string.Format("{0},", b.ToString())); } Console.WriteLine(""); } }
根據上述的8個例子可以得出如下結果:
1、補位式轉化、即少位數據類型向多位數據類型轉化 (例1、例2、例3、例4)
所補位與操作數的數據類型有關,與目標數據類型無關。
如果操作數是有符號的數據類型,所補位一律為操作數的符號位,如果操作數是無符號的數據類型,所補位一律為0。
2、截位式轉化、即多位數據類型向少位數據類型轉化 (例5、例6)
該轉換方式就是簡單的截取有效數位(即丟棄高位),和操作數的類型無關。
3、轉換符號位式轉化、即相同位數有無符號數據類型間的轉化(例7、例8)
發生該類型轉化時,只是最高位的意義發生了變化,進而可能導致結果改變。
三、整形與浮點型間的類型轉換
有了上面的基礎、再理解整形與浮點型的轉換就容易多了。
由整形向浮點型轉換的時候,如果整形數據的值過大或過小,就可能損失一些最低的有效位,造成精度損失(float的精度只有7位,double15~16位)。
四、參考資料:
原碼:http://baike.baidu.com/view/60480.htm
補碼:http://baike.baidu.com/view/377340.htm
最后祝園友們,中秋快樂(雖然晚了,但是誠意還在吧)。
新聞熱點
疑難解答