CSAPP Lab1:Data Lab
说明
这是CSAPP即《深入理解计算机系统》的配套实验一(网址link.),本实验有助于了解整形与单精度的原理与实现,也对熟悉位运算大有帮助。
先来一张完成实验的截图
话不多说,开整!
INT部分
规则
仅使用0-255的整型数,只能用本地变量,还有每一题允许使用的一些运算符。
bitXor
要求仅用~和&实现异或^运算,下面的代码是基于简单的离散数学知识
第一个公式把异或拆分为& | ~ 的组合,第二个用~ &实现 |,这就是本题的思路。
/*
* bitXor - x^y using only ~ and &
* Example: bitXor(4, 5) = 1
* Legal ops: ~ &
* Max ops: 14
* Rating: 1
*/
int bitXor(int x, int y) {
int a = ~(x&~y);
int b = ~(~x&y);
return ~(a&b);
}
tmin
要求返回最小的二进制补码(2’s complement)整数
− 2 31 \\{-2^{31}} −231是int最小可以表达的十进制数,它的十六进制形式是0x80000000,又因为只能用0-255以内的数,故用0x8左移28位来得到。
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
int a = 0x8;
return a<<28;
}
isTmax
如果传入的x是二进制补码表示的最大的数,那么返回1,否则返回0
思路就是最大的数加一会溢出到负数,而只有最大的数和0xffffffff加一之后会改变符号位。把x与x+1异或再取反,如果是最大数或0xffffffff,那么会得到0x0,其它的数都不会得到0x0(只考虑符号位,符号位必定相同,异或后为0,取反后为1,故不为全0),再确认加一之后的数不是0就好(由0xffffffff+1得到)。
/*
* isTmax - returns 1 if x is the maximum, two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
*/
int isTmax(int x) {
int overflow = x+1;
int res = ~(overflow^x);
return !res&!!overflow;
}
allOddBits
如果所有的奇数位都是1,则返回1;否则,返回0
注意只说了奇数位都要求为1,没规定偶数位的取值,所以先把奇数位提取出来,再和0xaaaaaaaa(奇数位全1,偶数位全0)异或比较。
/*
* allOddBits - return 1 if all odd-numbered bits in word set to 1
* where bits are numbered from 0 (least significant) to 31 (most significant)
* Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
*/
int allOddBits(int x) {
int a = 0xaa;
int a_8 = a<<8;//0x0000aa00
int low_16 = a|a_8;//0x0000aaaa;
int high_16 = low_16<<16;//0xaaaa0000
int num = low_16 | high_16;//0xaaaaaaaa
int check = (x&num)^num;
//如果check是0 则满足要求 返回1
return !check;
}
negate
实现取反,补码的定义,注意正数取反再加一也等于对应负数,而不只是负数取反再加一等于对应正数。
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x+1;
}
isAsciiDigit
如果x在大于等于0x30且小于等于0x39就返回1;否则,返回0.
思路是用减法(或者说加上负数),需要注意的是,为了统一最高的符号位所做的一点小改动。
/*
* isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
* Example: isAsciiDigit(0x35) = 1.
* isAsciiDigit(0x3a) = 0.
* isAsciiDigit(0x05) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 3
*/
int isAsciiDigit(int x) {
int neg_30 = ~0x30+1;
int neg_3a = ~0x3a+1;
int res1 = (x+neg_30)>>31; //大于等于0x30则为全0 否则全1
int res2 = (x+neg_3a)>>31;//小于等于0x39则为全1 否则全0
int res = ~res1&res2;//符合为全1 否则全0
return !!res;//注意第二个取反时用的是3a不是39,因为这样统一了小于等于的符号
}
conditional
实现类似三目运算符 x ? y : z
先对x取两次非,赋值给check,这样就得到了逻辑值(如果x是0,那两次非之后还是0;如果是非0,那两次非之后是1);第二行的先把check左移31位再右移31位是为了得到0x00000000或者0x11111111,注意有符号的数右移是逻辑的;最后再用(check&y) | ((~check)&z)取出想要的部分。
/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
int conditional(int x, int y, int z) {
int check = !!x;//化为逻辑值
check = (check<<31)>>31;//x为0时 check为0 否则check为0xffffffff 注意右移是逻辑的
return (check&y) | ((~check)&z);
}
isLessOrEqual
题目就是返回x小于等于y的真假
分为判断等于和判断小于两块,判断等于还是用异或,而判断小于有些麻烦:先观察两者符号位,如果x与y的符号位相同,那么就用减法(加相反数),这保证了不会溢出;如果符号位不同,那么虽然因为可能溢出不能用前一种方法,但是可以直接判断出x是否小于y,只有x的符号位是1,且y的符号位是0时才小于。具体细节下面代码有注释。
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
int equal_flag = ! (x^y);//相等为1 否则为0
int x_highest = x>>31;
int y_highest = y>>31;
int same_highest = x_highest^y_highest;//同号全0 否则全1
int x1_y0 = !!x_highest & !y_highest;//在x的最高位为1 且y的最高位为0时为1 否则为0 这是考虑了溢出
int neg_y = ~y+1;
int x_min_y = x+neg_y;
int hightest_bit = x_min_y>>31;//若小于则为全1 否则全0
hightest_bit = !!hightest_bit;//若小于则为1 否则0
return equal_flag | (~same_highest