前言
本篇博客以“SSD6-Exercise2-Data Lab: Manipulating Bits”为例,分析在对C语言中的整数采用补码(two’s-complement)编码的机器上,其整数运算的特性。
补码
定义
最常见的有符号数的计算机表示方式就是补码(two’s-complement)形式。在这个定义中,将字的最高有效位解释为负权(negative weight),我们用函数B2T(Binary to Two’s-complement的缩写,长度为 w)来表示。
如:
B2T ([0001]) = -0 + 0 + 0 + 1 = 1
B2T ([0101]) = -0 + 4 + 0 + 1 = 5
B2T ([1011]) = -8 + 0 + 2 + 1 = -5
B2T ([1111]) = -8 + 4 + 2 + 1 = -1
定理1
B2T ([11···1]) = -1
证明:假设B2T ([11···1]) 共有w位,则其值为 -2^(w-1) + 2^(w-2) + ··· + 2^0
. 根据等比数列求和公式,易证该值为-1.
定理2
对于
w
位的补码B2T来说,其边界值Tmax与Tmin分别为:Tmax = B2T ([01···1]) = 2^(w-1) - 1
Tmin = B2T ([10···0]) = -2^(w-1)
即有:~Tmax = Tmin
整数运算
我们先以表格的形式,宏观介绍C语言中的位级运算、逻辑运算和移位运算。
运算种类 | 运算符 | 主要说明 |
---|---|---|
位级运算 | |, &, ~, ^ | 对应于布尔运算中的OR, AND, NOT, EXCLUSIVE-OR |
逻辑运算 | ||, &&, ! | 对应于命题逻辑中的OR, AND, NOT |
移位运算 | <<, >> | 分为左移与右移,右移运算包括逻辑右移与算数右移 |
!
与~
有什么区别?
注意:逻辑运算很容易和位级运算相混淆,但是它们的功能是完全不同的。
逻辑运算中认为所有非零的参数都表示
TRUE
,而参数0表示FALSE
.逻辑运算的结果为一个布尔值,而位级运算的结果依然为一个数.
逻辑运算的运算符常称为
与
、或
、非
,而位级运算的运算符常称为与
、或
、取反
、异或
.
因此,!
是逻辑运算中的非
运算符,而~
是位级运算中的取反
运算符。
逻辑右移与算术右移
我们先来看左移运算<<
.
对操作数
x
执行x<<k
运算,即x
向左移动k
位。此运算会丢弃最高的k
位,并在右端补k
个0
.
相应而言的右移运算>>
.
对操作数
x
执行x>>k
运算,即x
向右移动k
位。此运算会丢弃最低的k
位,那么在左端需要补充的k
个位是什么呢?若执行逻辑右移,则补充
k
个0
,这类似于左移运算.若执行算术右移,则补充
k
个最高有效位
的值。
且几乎所有的编译器/机器组合都对有符号数使用算术右移,对无符号数采用逻辑右移。
运算特性
我们通过完成这下面这10个函数,来体会补码的整数运算特性。
/*
* bitAnd - x&y using only ~ and |
* Example: bitAnd(6, 5) = 4
* Legal ops: ~ |
* Max ops: 8
* Rating: 1
*/
int bitAnd(int x, int y) {
return ;
}
/*
* bitOr - x|y using only ~ and &
* Example: bitOr(6, 5) = 7
* Legal ops: ~ &
* Max ops: 8
* Rating: 1
*/
int bitOr(int x, int y) {
re