-
Csapp–datelab学习
-
bitXor
题目
/*
- bitXor - x^y using only ~ and &
- Example: bitXor(4, 5) = 1
- Legal ops: ~ &
- Max ops: 14
- Rating: 1
*/
分析,解题过程和思路及学习
离散数学中异或(对称差),符号为⊕。
运算法则:a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
其中非(¬)与非运算(~)对应,合取(∧)和与运算(&)对应,析取(∨)与或运算(|)对应。
由¬(A∨B)⇔¬A∧¬B(德摩根律)可知a⊕b = ¬(¬(¬a ∧ b) ∧ ¬(a ∧¬b))
对应可知计算机中a^b = ((~a & b) & ~(a & ~b))
代码
int bitXor(int x, int y) {
return ~(~(~x & y) & ~(x & ~y));
}
tmin
题目
/*
- tmin - return minimum two’s complement integer (返回最小二进制补码)
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 4
- Rating: 1
*/
分析,解题过程和思路及学习
原码:最高位为符号位,0表示正数,1表示负数。
反码:正数的反码等于本身,负数的反码除符号位外,各位取反。
补码:正数的补码等于本身,负数的补码等于反码+1。
1000 0000:负0;
负0,实际是白占一坑,就把它用来表示-128了。
-128的原码 10000000 (-128,进位被舍去)
-128的反码 11111111(负数的非符号位取反)
-128的补码 11111111+ 1=10000000(反码+1),这里实际上真正相加的是11111111后面的7位,第1位是符号位始终不会变,所以,当进到第8位的时候,就表示溢出被舍弃。
所以最小的二进制补码为10000000,C语言中int类型占4字节,即32位,所以对1左移31位来构造最小补码。
代码
int tmin(void) {
int a = 1;
return a << 31;
}
isTmax
题目
/*
- isTmax - returns 1 if x is the maximum, two’s complement number,and 0 otherwise
- Legal ops: ! ~ & ^ | +
- Max ops: 10
- Rating: 1
*/
分析,解题过程和思路及学习
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假
逻辑运算值的确定 运算对象为非0表示逻辑值,值为1;运算对象为表示逻辑假,值为0。
内存中的数值为补码表示,所以0xFFFFFFFF是一个负数的补码。负数从补码求原码,最高符号位不变,保持 1, 其余各位求反,末尾加1,
代码
int isTmax(int x) {
int neg1;
neg1 = !(~x); // 如果x为-1, 则neg1为1,否则neg1为0,这里是为了排除-1的干扰
return !((~(x+1)^x)|neg1); // 给x加1,再翻转,最后和自身取异或,如果x为Tmax,则返回1,否则返回0
}
x
01111111(127的原码) 10000001(-1的补码)
01111111(127的反码) 11111110(-1的反码)
01111111(127的补码) 11111111(-1的补码)
~x(内存中的数值为补码表示)
10000000 00000000
neg=!~x
00000000 00000001
x+1
11111111 00000000
~(x+1)
00000000 11111111
y=~(x+1)^x
10000000 00000000
y|neg
10000000 00000001
allOddBits
题目
- /*
- 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
*/
分析,解题过程和思路及学习
a = 10101010 10101010 10101010 10101010 (0xAAAAAAAA)
a ^a =0
0xAAAAAAAA & 奇数位全为1的二进制数等于0xAAAAAAAA,其结果与0xAAAAAAAA异或为0。
0xAAAAAAAA & 奇数位不全为1的二进制数不等于0xAAAAAAAA,其结果与0xAAAAAAAA异或不为0。
代码
int allOddBits(int x) {
int a = 0xAA | 0xAA << 8;
a = a | a << 16;
return !(a^(a&x));
}
negate
题目
/*
- negate - return -x
- Example: negate(1) = -1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 5
- Rating: 2
*/
分析,解题过程和思路及学习
0(00000000)的负数为0(00000000)。
1(00000001)的负数为-1(10000001)。
负数的二进制一般通过正数反推导出,即(正数二进制 -> 取反 -> 补码(加1))
~x + x = -1,即~x+1=-x。
1(00000001)取反 = 11111110,+1 = 11111111(-1的补码)
代码
int negate(int x) {
x = (~x) + 1;
return x;
}
isAsciiDigit
题目
/*
- 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
*/
分析,解题过程和思路及学习
0x30(00110000)
0x39(00111001)
0x30 <= x <= 0x39,即x - 0x30 => 0且0x39 - x >= 0,即满足x的数符号位为0。
a - b可以看作 a + (-b),而 -b = ~x + 1。
代码
int isAsciiDigit(int x) {
int a = x + (~(0x30) + 1);
int b = 0x39 + (~x+1);
a = a >> 31;
b = b >> 31;
return !(a | b);//保证条件同时满足
}
conditional
题目
/*
- conditional - same as x ? y : z
- Example: conditional(2,4,5) = 4
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 16
- Rating: 3
*/
分析,解题过程和思路及学习
主要是实现三目运算。通过判断x,确定返回值,可以用!!x判断,!!x = 0 即 false 返回y,!!x = 1 即 true返回z。返回原值可以& -1实现,返回0可以用& 0实现。
代码
int conditional(int x, int y, int z) {
int a = !!x;
int b = ~a + 1;
return (b & y) | (~b & z);
}
isLessOrEqual
题目
/*
- isLessOrEqual - if x <= y then return 1, else return 0
- Example: isLessOrEqual(4,5) = 1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 24
- Rating: 3
*/
分析,解题过程和思路及学习
y - x => 0, 即y + (~x+1) =>0。所得结果符号位为0。
代码
int isLessOrEqual(int x, int y) {
int a = y + (~x + 1);
int b = a >> 31;
return !b;
}
logicalNeg
题目
/*
- logicalNeg - implement the ! operator, using all of
-
the legal operators except !
- Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
- Legal ops: ~ & ^ | + << >>
- Max ops: 12
- Rating: 4
*/
分析,解题过程和思路及学习
实现!运算。当x != 0时,返回0,当x = 0时,返回1。
一个非0数和其相反数异或后,结果的符号位为1。而0的相反数也为0,异或后符号位为0。
将二进制数向左移位操作,高位溢出则丢弃,低位补0
右移位运算中,无符号数和有符号数的运算并不相同。对于无符号数,右移之后高位补0;对于有符号数,符号位一起移动,正数高位补0,负数高位补1
代码
int logicalNeg(int x) {
int a = x | (~x + 1);//符号位为1,即是有符号位负数
int b = a >> 31;//b为0或-1
return b+1;
}