datalab简介
这个lab要求使用高度受限的C语言操作,来实现一些简单的逻辑功能,以及补码、浮点数的相关操作函数。比如,只能使用位级运算符来实现计算一个数的绝对值,并且必须是straightline code(代码中不能使用if else、switch、while、goto等)。
这个lab的主要目的是帮助我们 理解数据的位级表示和位级操作
完成datalab
1. bitxor
功能:对于入参 int x,y 。使用 ~和&运算符 实现 x ^ y
解题思路:
我们可以快速的构建 c = a ^ b 的 真值表
。
a | b | c |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
然后将 真值表
转化为 卡诺图
,这样可以得到
a ^ b == (~a&b | a&~b)
然后根据 摩尔根定律
~~(a ^ b) == a ^ b == ~(~(~a&b) & ~(a&~b))
这里只涉及a,b两个元的逻辑关系,比较简单,所以其实没有必要使用真值表和卡诺图。但是要注意这种解题的思路,当涉及到多个元的逻辑关系时。卡诺图是将逻辑关系简化转换为 &和| 的利器。
旁注:卡诺图化简法
/*
* 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 m = x & ~y;
int n = ~x & y;
return ~(~m & ~n);
}
2.isTmax
功能:对于入参 int x,若x是最大值则返回1,否则返回0。
解题思路:
32位int(补码表示)的最大值 TMax
为 0x7fffffff
,我们发现 TMax + 1
即为 0x80000000
(1 << 31)。
所以若 x+1 == (1 << 31)
即说明 x 是 TMax
。
这里利用异或来做相等判断。 (a ^ a = 0
)
/*
* 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) {
return !((1 + x) ^ (1 << 31));
}
3.allOddBits
功能:对于入参 int x 的位级表示,若其每个奇数位都为1则返回1,否则返回0。
解题思路:
构造一个 0xAAAAAAAA
。
若x所有的奇数位均为1,那么有 x & 0xAAAAAAAA = 0xAAAAAAAA
。
/*
* 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 m = 0xAA;
m = (m << 8) | m;
m = (m << 16) | m;
return !((x & m) ^ m);
}
4. negate
功能:对于入参 int x,返回 -x。
解题思路:
补码的反码即:~x + 1
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
5. isAsciiDigit
功能:对于入参 int x,如果它的值可以表示Ascii字符0到9,则返回1,否则返回0。
解题思路:
0x30 ~ 0x39 (Ascii码0~9的十六进制值),的二进制表示为 0011 0000
~ 0011 1001
观察二进制位,我们可以发现。若第4位二进制位为 0
则其左侧第三位可以取任何值,若第4位二进制位为 1
,则其左侧第三位只能为 001
。
/*
* 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 p1 = !((x >> 1) ^ 0x1c);
int p2 = !((x >> 3) ^ 0x6);
return p1 | p2;
}
6. conditional
功能:对于入参 int x,y,z。如果 x 非 0 则返回 y,若为 0 则返回 z。
解题思路:
通过 !!x
可以将 x 转为 0 或 1 。由于后面需要返回 y 或 z 的值,所以这里将 0 转为 0x00000000 , 1 转为 0xffffffff 。(32位全0,32位全1)
/*
* 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 cond = !!x;// covert x to 0 or 1
int mask = (cond << 31) >> 31;//0 --> 0x0, 1 --> 0xffffffff
return (mask & y) | (~mask & z);
}
7. isLessOrEqual
功能:对于入参 int x,y。若 x <= y 返回 1,否则返回 0。
解题思路:
对于 x 和 y ,考虑以下情况:
若其符号位相同,则有若x < y,即 x - y < 0 ,即x + (~y + 1) < 0
若其符号位不同,则有若x < y,即 x < 0 且 y > 0
若 x = y,则 x ^ y = 0
如果不考虑符号位是否相等,贸然通过 x 与 y 的差值来进行判断是不对的,因为符号不同 x 与 y 的差值计算可能会溢出
/*
* 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 x_sign = (x >> 31) & 0x1;
int y_sign = (y >> 31) & 0x1;
int xSign_eq_ySign = !(x_sign ^ y_sign);
//符号位相同时, x < y --> x - y < 0 --> x + ~y + 1 < 0
int c1 = xSign_eq_ySign & (0x1 & ((x + ~y + 1) >> 31));
//x < y --> x < 0, y > 0
int c2 = x_sign & !y_sign;
// x = y时
int c3 = !(x ^ y);
return c3 | c2 | c1;
}
8. logicalNeg
功能:对于入参 int x,若x为0则返回1,否则返回0。(即实现运算符 !)
解题思路:
若 int x=0 ,则有 x 有以下特征:
a. x 的符号位为0;
b. -x,即 ~x + 1 的符号位仍为0;
/*
* 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
*/
int logicalNeg(int x) {
int x_sign = (x >> 31) & 0x1;
//符号位为0,且x=0 --> !x 返回1
//x=0时, -x的符号位仍然是0
int x2_sign = ((~x + 1) >> 31) & 0x1;
int c1 = (~x_sign & 0x1) & (~x2_sign & 0x1);
return c1;
}
9. howManyBits
功能:对于入参 int x,返回一个可以表示其值的最小二进制位长度。
解题思路:
12(01100)
-5(1011)
0 (0)
1 (-1)
通过观察总结,我们可以发现如下规律:
a. 对于正数,结果为二进制位为中最高位为1的所在位置加1。
b. 对于负数我们可以取 ~x
,结果为 -x
二进制位中最高位为1的所在位置加1。
我们通过 二分查找
来找到二进制位中最高位为1的所在位置。
本题最好通过纸笔计算,这样才更容易理解下面代码的工作流程。
/* howManyBits - return the minimum number of bits required to represent x in
* two's complement
* Examples: howManyBits(12) = 5
* howManyBits(298) = 10
* howManyBits(-5) = 4
* howManyBits(0) = 1
* howManyBits(-1) = 1
* howManyBits(0x80000000) = 32
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int howManyBits(int x) {
//x为负数时,取~x
int x_sign = (x >> 31) & 0x1;
int mask = (x_sign << 31) >> 31;
x = (mask & ~x) | (~mask & x);
//
//采用二分查找法,找出x最高位为1的位置
int left_shift = 16;
int k = !!(x >> left_shift) << 4;//0 | 16
left_shift = (left_shift - 8) + k;//8 | 24
k = !!(x >> left_shift) << 3;//0 | 8
left_shift = (left_shift - 4) + k;//4 | 12 | 20 | 28
k = !!(x >> left_shift) << 2;//0 | 4
left_shift = (left_shift - 2) + k;//2 | 6 | 10 | 14 | 18 | 22 | 26 | 30
k = !!(x >> left_shift) << 1;//0 | 2
left_shift = (left_shift - 1) + k;//1 | 3 | 5 | 7 | 9 | 11 | ... | 29 | 31
k = !!(x >> left_shift);
//没有涵盖x=0的情况(需要特殊处理)
int high_bit_one_index = k + left_shift;
int is_x_zero = !(x ^ 0);
mask = (is_x_zero << 31) >> 31;
return (mask & 0x1) | (~mask & (high_bit_one_index + 1));
}
10. floatScale2
功能:对于入参 unsigned uf,以 float 来解释其二进制位,若 uf 为 NaN 或 无穷大 则直接返回,否则计算 uf * 2 并返回。
解题思路:
参见IEEE浮点标准。
/*
* floatScale2 - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representation of
* single-precision floating point values.
* When argument is NaN, return argument
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatScale2(unsigned uf) {
unsigned exp = (uf & 0x7f800000) >> 23;
unsigned frac = uf & 0x7fffff;
//NaN 和 无穷大时,返回uf
if(exp == 255)
return uf;
//非规格化时
if(exp == 0){
//frac = (frac << 1) & 0x7fffff;
return (uf & 0x80000000) + (exp << 23) + (frac << 1);
}
//规格化时
return (uf & 0x80000000) + ((exp+1) << 23) + frac;
}
11. floatFloat2Int
功能:对于入参 unsigned uf,以 float 来解释其二进制位,并尝试将其转换为 int 返回,若溢出直接返回 0x80000000。
解题思路:
参见IEEE浮点标准。
/*
* floatFloat2Int - Return bit-level equivalent of expression (int) f
* for floating point argument f.
* Argument is passed as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point value.
* Anything out of range (including NaN and infinity) should return
* 0x80000000u.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
int floatFloat2Int(unsigned uf) {
unsigned exp = (uf & 0x7f800000) >> 23;
int bias = 127;
//小于1
if(exp < bias)
return 0;
//溢出
if(exp >= (bias + 31))
return 0x80000000;
int v = 1 << (exp -bias);
int s = (uf >> 31) & 0x1;
if(s)
return -v;
return v;
}
12. floatPower2
功能:对于入参 int x,返回 2.0 的 x 次方的位级表示。
解题思路:
参见IEEE浮点标准。
浮点数表示的标准表达式为,则对于 float 值 2.0 ,s=0 M=1 E=1
。
类型 | 偏置值Bias | ||
---|---|---|---|
规格化的 | exp - Bias | 1 + frac | |
非规格化的 | 1 - Bias | frac |
单精度float类型的偏置值Bias 为 127, float 值 2.0 为规格化的。所以我们可以得出 exp=128 frac=0
即float 2.0 的二进制表示为 0 10000000 000000000000000
2.0的x次方,即E = E * x
。
感觉本题的解题思路没有问题,但是测试没有通过,提示进入了无限循环。
/*
* floatPower2 - Return bit-level equivalent of the expression 2.0^x
* (2.0 raised to the power x) for any 32-bit integer x.
*
* The unsigned value that is returned should have the identical bit
* representation as the single-precision floating-point number 2.0^x.
* If the result is too small to be represented as a denorm, return
* 0. If too large, return +INF.
*
* Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatPower2(int x) {
//normalize exp [1, 254] --> E [-126, 127]
int E = 1;
E = x * E;
if(E < -126)
return 0;
if(E > 127)
return 0x7f800000;
int bias = 127;
int exp = E + bias;
return exp << 23;
}
测试报告
很遗憾,最后一个函数
floatPower2
没有通过,网上找了很多资料,也没有找到解决的办法,如果大家有正确的解法,希望能告诉我一下,谢谢。
lab资料
Data Lab [Updated 12/16/19] (README, lab 操作指南, lab 修订记录, lab 下载)