今天一打开博客,看到左上角的园龄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
最后祝园友们,中秋快乐(虽然晚了,但是诚意还在吧)。