每个编程语言都存在变量类型和类型之间的转换问题,一般很多书籍都提供了类型之间怎样进行转换的知识,但是很少介绍这些类型转换的背后原理。有人会问,我只要知道怎样进行转换这些类型就可以了,有必要了解这些知识么?我的觉得还是很有必要了解,只有了解了这些类型转换的原理,我们在编程时才能避免一些坑。例如:
- float sum_element(float a[], unsigned length){
- int i;
- float result = 0;
- for(i = 0; i <= length -1; i++){
- result += a[i];
- }
- return result;
- }
以上代码看着好像是没问题,但是隐藏着一个很大的 bug,这个问题就是因为类型转换导致的。在这里我们先买个关子,具体会出现什么问题?为什么会出现这个问题?我们先介绍本文主要内容后再回答这些问题。
首先介绍计算机中的整型是如何表示的,了解整数在计算机中的表示才能知道整数的转化原理。整数在计算的表示方式有:补码、反码还有原码。目前大部分计算机都采用补码来表示整数。整数分为有符号和无符号,这两种方式在计算机里的表示不同。
这种形式不包含负数,只表示 0 和正整数。采用补码表示时,其实就是将数字转化为对应的二进制表示。
例如:12345 对应的补码表示是:00 00 30 39(16 进制)。
无符号的补码编码形式:
$$B2U_w(x) = \sum_{i=0}^{w-2}x_{i}2^{i}$$
如果要将二进制转换为 10 进制数,就按照上面的式子计算。
例如 $B2U_4([0001]) = 02^2 + 02^0$
补码使用最高位表示符号,1 表示整数,0 表示负数。因此正负数各占一半,因为 0 是非负数 (正整数),因此负数的范围会比正整数的范围大。例如 c 语言中 int 的取值范围是 $-2^7$ ~$2^7-1$。
有符号的补码编码形式如下:
$$B2T_w(x) = -x_{w-1}2^{w-1}+\sum_{i=0}^{w-2}x_{i}2^{i}$$
同样二进制数转换为 10 进制数则采用以上公式计算。
在 c 语言标准中并没有要求用补码形式来表示有符号的整数,但几乎所有的机器都这么做了。而 java 的标准非常明确要求使用补码表示。
在 C 语言中处理同样字长的有符号数和无符号数字之间相互转化的规则是:保持位值不变,只是改变了解释这些位的方式,因此可能得到不同的数值。例如:
- short int sv = -12345;
- unsigned short uv = (unsigned short) sv;
- show_short(sv); //显示二进制
- show_unshort(uv); //显示二进制
其输出结果如下:
- [zjl@ ~/workplace/]$ ./show_bytes
- cf c7
- cf c7
从结果可以看出 sv 和 uv 表示的二进制是一样的,但是具体的值会发生变化。
C 语言中有符号数字与无符号数值进行计算时,首先将有符号数值转化为无符号数值,然后运算。这种方式对于标准的运算方式来说并无差异,但是如果是逻辑关系运算符时,就会出现问题。
例如:
- -1 < 0
- -1 < 0u
以上两个例子中输出的结果并不是都为真。 -1 <0 因为两个都是有符号数据,直接比较。而 - 1 < 0u, 因为后面的值是无符号类型,因此将 1 转换为无符号类型。此刻 1 变为了 2147483647U, 故出现了 - 1> 0U 的现象。java 中不存在无符号整型类型,因此也不存在以上问题。
1)短类型转化为长类型
对于无符号的转化为一个更大的数据类型,只要简单的在原来二进制值前添加 0 则可,这种运算叫做零扩散。
例如:
- unsigned short int v = 1; //二进制为 00 01
- unsigned int iv = vl; //二进制变为 00 00 00 01
对于有符号数值,将一补码转换为一个更大数值类型可以执行符号拓展,在补码之前补充最高位值得副本,即将 $[x_{w-1},x_{w-2},...,x_0]$ 转换为 $[x_{w-1},x_{w-1},...,x_{w-1},x_{w-2},...,x_0]$.
例如:
- short sx = -12345; // cf c7
- int x = sx; // ff ff c7 值为:53191
如果是负数,前面补充 1,根据补码编码计算公式,最后得到的值不变。举个例子:比如将 3 位数变为 4 位数,位向量 [101] 的值为 - 4 + 1 = 3。对它进行符号拓展,得到位向量为 [1101], 表示的值为 - 8 + 4 + 1 = -3。(具体证明公式感兴趣的可以参考《深入理解计算机系统》P49) 同样如果是整数,补充 0,最后的结果也是不变。
2) 长类型转化为短类型
长类型转换为短类型则采用截断方式,即根据短类型长度从长类型中截取最低位部分。$[x_{w-1},x_{w-2},...,x_0]$ 转为为 k 位的数字类型,变为 $[x_{k-1},x_{k-2},...,x_0]$
例如:
- int i = -12345; // ff ff cf c7
- short i = (short) i; // 值为-12345, 补码:cf c7
知道了 c 语言的转换原理以后,我们再回头看看文章开头提出的问题。
- float sum_element(float a[], unsigned int length){
- int i;
- float result = 0;
- for(i = 0; i <= length -1; i++){
- result += a[i];
- }
- return result;
- }
当 length = 0 作为输入时,这段代码存在两个问题:
[1]《深入理解计算机系统》
来源: http://www.cnblogs.com/jaylon/p/6550308.html