数据类型转换 符号扩展 (二)

2014-11-24 10:54:55 · 作者: · 浏览: 1
80
$FFFF_FF80
%1111_1111_1111_1111_1111_1111_1000_0000

$28
$0028
$0000_0028
%0000_0000_0000_0000_0000_0000_0010_1000

$9A
$FF9A
$FFFF_FF9A
%1111_1111_1111_1111_1111_1111_1001_1010

$7F
$007F
$0000_007F
%0000_0000_0000_0000_0000_0000_0111_1111

n/a
$1020
$0000_1020
%0000_0000_0000_0000_0001_0000_0010_0000

n/a
$8086
$FFFF_8086
%1111_1111_1111_1111_1000_0000_1000_0110

处理无符号二进制数的时候,可以使用零扩展(zero extension)来将小位数的无符号数扩展到大位数的无符号数。零扩展非常简单——只需要用零来填充大位数操作数的高端各个字节即可。例如,为了将8位数$82零扩展到16位,只需要在高端字节中插入零,即得到$0082。

表2-6 零扩展举例

8位
16位
32位
二进制补码表示

$80
$0080
$0000_0080
%0000_0000_0000_0000_0000_0000_1000_0000

$28
$0028
$0000_0028
%0000_0000_0000_0000_0000_0000_0010_1000

$9A
$009A
$0000_009A
%0000_0000_0000_0000_0000_0000_1001_1010

$7F
$007F
$0000_007F
%0000_0000_0000_0000_0000_0000_0111_1111

n/a
$1020
$0000_1020
%0000_0000_0000_0000_0001_0000_0010_0000

n/a
$8086
$0000_8086
%0000_0000_0000_0000_1000_0000_1000_0110

大多数高级语言编译器会自动处理符号扩展与零扩展,以下C语言的例子说明了它们是如何工作的:

signed char sbyte; // C语言中的字符类型是一个字节

short int sword; // C语言中的短整型一般是16位

long int sdword; // C语言中的长整型一般是32位

. . .

sword = sbyte; //自动将8位值符号扩展到16位

sdword = sbyte; //自动将8位值符号扩展到32位

sdword = sword; //自动将16位值符号扩展到32位

语言(例如Ada)在从小数据类型转换到大数据类型时需要显式转换(explicit cast)。查一下所用语言的参考手册就知道这种显式转换是不是必需的了。要求提供显式转换的语言的优点在于编译器永远不会在程序员不知情的情况下做任何事情。如果你没有提供必要的转换,编译器会给出一个诊断消息,让你知道程序还需要改进。

符号扩展和零扩展,有一点需要明确的是,它们是需要付出代价的。将一个小整型赋值给一个大整型可能会比在同样大小的整型变量间传输数据需要更多的机器指令(执行时间更长)。因此,在一个数学表达式或者一条赋值语句中混合使用不同大小的变量要小心。

符号缩减,即将一个某位数转换为值相同但位数变小的数,比较麻烦。符号扩展永远不会失败,使用符号扩展,一个m位有符号数永远可以转换为一个n位数(这里n>m)。不幸的是,在m

将一个数值正确地符号缩减,必须要检查需要丢弃的高端字节。首先,这些高端字节必须是全零或者$FF,如果它们包含其他值,我们就无法对这个数进行符号缩减。其次,最终结果的最高位必须与被丢弃的所有位一致。以下就是一些从16位数转换到8位数的例子:

$FF80 (%1111_1111_1000_0000) 可以被符号缩减为 $80 (%1000_0000).

$0040 (%0000_0000_0100_0000) 可以被符号缩减为 $40 (%0100_0000).

$FE40 (%1111_1110_0100_0000) 不能被符号缩减为8 位

$0100 (%0000_0001_0000_0000) 不能被符号缩减为8 位

级语言里使用缩减有点困难,有些语言,譬如说C语言,会直接将表达式的低端部分存储到比较小的变量中,并将高端部分丢弃(在最好的情况下,C编译器可能会在编译过程中给出一个警告,提示可能会出现的精度损失)。你可以采取措施来让编译器停止抱怨,但是它仍然不会检查数值的有效性。以下是C语言中符号缩减的典型代码:

signed char sbyte; // C语言中的字符类型是一个字节

short int sword; // C语言中的短整型一般是16位

long int sdword; // C语言中的长整型一般是32位

. . .

sbyte = (signed char) sword;

sbyte = (signed char) sdword;

sword = (short int) sdword;

语言中,唯一安全的解决方案就是在将表达式的结果值存储到一个小变量中之前,将该结果值与某个上下边界值进行比较。不幸的是,如果需要经常做这种操作,代码会变得比较笨拙。以下就是加上这些检查之后的转换代码:

if( sword >= -128 && sword <= 127 )

{

sbyte = (signed char) sword;

}

else

{

// 报告错误

}

// 另一种方案,使用断言:

assert( sword >= -128 && sword <= 127 )

sbyte = (signed char) sword;

assert( sdword >= -32768 && sdword <= 32767 )

sword = (short int) sdword;

易见,这让代码变得丑陋。在C/C++中,你可能会倾向于将它们编写为宏(#define)或者函数,以提高代码的可读性。

有些高级语言(例如Pascal和Delphi/Kylix)会自动进行符号缩减,还会检查结果来确保它适用于目标操作4。这些语言在越界违例发生的时候会产生某种类型的异常(或者停止程序的运行)。当然了,如果你想加入纠错代码,要么就需要写点异常处理代码,要么就使用前面C语言例子中使用的if语句序列。