Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1509650
  • 博文数量: 228
  • 博客积分: 1698
  • 博客等级: 上尉
  • 技术积分: 3241
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-24 21:49
个人简介

Linux

文章分类

全部博文(228)

文章存档

2017年(1)

2016年(43)

2015年(102)

2014年(44)

2013年(5)

2012年(30)

2011年(3)

分类: LINUX

2014-05-09 08:33:06

关于符号扩展的一些整理,原文:
http://blog.csdn.net/weizhee/article/details/1351455
http://www.cppblog.com/deercoder/articles/98080.html
http://blog.csdn.net/andy572633/article/details/7322893

最近由于工作需要展望性地研究了一下64位编程。无非就是一些int,long和pointer间转换时的注意事项。如果就兴趣的话可以去搜索一下ILP32和LP64数据模型的相关知识。我略有小得的却是间接地对基本类型间的相互转换加深了认识。这里我想说一下符号扩展的问题。符号扩展不只是在64位编程时应该注意的问题,在32位编程时同样需要注意。请先看下面的例子:

#include
int main(void)
{
 char c = 1<<7;
 short s;
 s = c;
 printf("%d/n",s);
 return 0;
}

由于c是有符号char类型,值是-128,s是有符号short类型,把c赋值给s后,程序输出-128似乎是理所当然的。
那么我们把c改成无符号数,程序变成如下:

#include
int main(void)
{
 unsigned char c = 1<<7;
 short s;
 s = c;
 printf("%d/n",s);
 return 0;
}

c是无符号char类型,值是128,s是有符号short类型,把c赋值给s后,程序输出128似乎是也理所当然的。
当我们让c是有符号的,s是无符号的时候,程序变成如下:

#include
int main(void)
{
 char c = 1<<7;
 unsigned short s;
 s = c;
 printf("%d/n",s);
 return 0;
}

此时程序输出65408,这是为什么呢?原因就在于符号扩展。

让我们来对比一下这3个程序中的c与s的16进制值:

    程序1    程序2     程序3
c  0x80     0x80       0x80
s  0xff80   0x0080  0xff80

当负的有符号char转换成有符号short时,符号位要进行扩展,即扩展位是全1而不是全0。
那么 对于程序3,为什么有符号char转换成无符号short也进行了符号扩展呢?其实这里也是有规则的:
当有符号char转换成无符号short时,有符号char首先转换成有符号short,再转换成无符号short。
这样问题才算清晰了,无符号0xff80的值即是65408。

上述规则对于char到long,short到long的转换都是适用的。 

C语言中,当不同类型的数据进行运算的时候,就会发生强制或隐式类型转换,通常是低精度的数据类型扩展到高精度的。有些时候,低精度的位数比较少,扩展到高精度的时候,就要在前面补充一些位。那么这些位是补0还是补1呢?这就涉及到无符号扩展和带符号扩展。

扩展的原则是:
1.有符号的数据类型,在向高精度扩展时,总是带符号扩展

2.无符号的数据类型,在向高精度扩展时,总是无符号扩展

怎么理解呢?首先来看一道题目,按此题目讲解完你就明白了!

char   ca=128;

unsigned  char  ucb=128;

unsigned  short   usc=0;

1)usc=ca + ucb; 

 printf("%x",usc);

2)usc=ca +(unsigned  short)ucb; 

 printf("%x",usc);

3)usc=(unsignedchar)ca + ucb;

printf("%x",usc);

4)usc=ca+(char)ucb;

printf("%x",usc);

,在1、2、3、4这4种情况下分别输出什么?

分析:

1)对于char类型,有符号,128已经溢出了,其二进制是 1000 0000,第一位会被当成符号位,也是就是说此时它是负数了,它扩展成unsigned short时,带符号位扩展,符号位为1,所以在前方补1,结果是 1111 1111 1000 000。 ucb类型为nsigned char,无符号,二进制是1000 0000,扩展成unsigned shor,无符号扩展,所以补0,结果是0000 0000 1000 0000,。相加结果为 1 0000 0000 0000 0000 由于unsigned short是二字节,舍弃最前面的1,所以得到0x0,

2)情况和1一样,只是将ucb显示强制转换为unsigned short,所以得到0x0,

3)ca先强制转换为unsignedchar,仍然是1000 0000,注意此时转换后已经是一个无符号数,所以再往unsigned  short 扩展时,为无符号扩展,结果为0000 0000 1000 0000,ucb扩展后

也是0000 0000 1000 0000,相加结果为 0000 0001 0000 0000,所以结果为0x100

4)ca转为unsigned short,带符号扩展,为1111 1111 1000 000,ucb先强制转换为char,然后再转为unsigned short,此时也要带符号扩展,所以也是1111 1111 1000 000,两数相加,得到 1 1111 1111 0000 0000 ,所以结果为0xff00

符号扩展和截断的几点认识:

1.      符号扩展。。

   short int a = -4;

   short int b = 8;

   print_binary(a);

   print_binary(b);

   unsigned short int c = a; //类型转换

   unsigned int d = a;

   print_binary(c);

   print_binary(d);

   return 0;

结果是:

11111111 11111100

00000000 00001000

11111111 11111100

11111111 11111111 11111111 11111100

请按任意键继续. . .

 

从结果可以看到:

1.类型转换时不改变位的标志的。

2.      符号扩展,将一个有符号的扩展的话是符号扩展。从short扩展到int,由于是负数,所以符号扩展,前面的都是111111……

 

 

2.截断处理:

int a = -4;

   short int b = a;

   int c = 4;

   short int d = c;

   print_binary(a);

   print_binary(b);

   print_binary(c);

   print_binary(d)

结果是:

11111111 11111111 11111111 11111100

11111111 11111100

00000000 00000000 00000000 00000100

00000000 00000100

请按任意键继续. . .

发现了,截断高位,不管符号,都要截断高位的部分。

 

3.乘法和除法:

当存储数据的位数不足以存储乘积的时候,会造成截断误差,通过截断,得到我们的结果不是想要的结果:看下例:

char a = 120;

   print_binary(a);

   char c = 120;

   print_binary(c);

   char d = a * c;

   cout << (short)d << endl;

   print_binary(d);

   int e = a * c;

   cout << e << endl;

   print_binary(e);

   return 0;

 

结果是:

 

01111000

01111000

64

01000000

14400

00000000 00000000 00111000 01000000

请按任意键继续. . .

看到了没有,对于一个char类型的数据,如果将两者的乘积还是保存为char,那么就有可能发生截断,产生误差。此例即为说明,而如果将结果用int来保存,可以看到完整的位表示,然后具体的截断也知道了。此时就可以得到正常的结果,因为有足够的位,没有造成损失。。



阅读(2315) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~