datalab
这是我们做的第一个lab,题解是早就写在pdf里的,只是一直没有写成博客。现在我们来一题一题看:
int bitAnd(int x,int y)
首先是 bitAnd,顾名思义就是按位与:全是1结果才是1,有0结果就是0.
因为这题只能用 |
和 ~
,
比如说两个数都是1,那么取反后他们都是0,或一下再取反后就为1;两个数中间有一个1有一个0,那么去反之后还是有一个1一个0,或以下为1,再取反之后为0;同理如果两个数都为0,取反之后都为1,或一下再取反则为0.
因此这囊括了所有情况
1 | int bitAnd(int x, int y) |
int getByte(int x,int n)
getByte这个函数要求我们再以十六进制计数的情况下以大端法取Byte,一个byte就是8bit,两位十六进制数。
首先我们先让n扩大8倍,因为一个n代表8个bit
然后我们让x右移n位,比如说n等于0就不移动,n=1时右移8位,n=2的时候右移16位,这样就能保证得到的数的倒数后8位(2进制形式)就是我们想要取得的byte
然后我们让其和 0xff 按位与。就能获得最后的答案
1 | /* |
int logicalShift(int x, int n)
这是让我们实现逻辑右移,也就是向右移动k位,数字丢弃最高的k位,并在左端补上k个0.
但是我们知道,这不是unsigned而是int类型的,直接使用右移符号使用的是算术右移。因此我们要创造出一个mask,长这样 0x0...0f...f
(n个0,32-n个1) 的掩码。
首先我们要获得 32-n这个数,因为直接用减号是非法的,我们可以用反码+1的方式取得n的负数然后再加上32. 在制作掩码的时候,如下图所示:
但是还有一种情况需要我们考虑,也就是当n=31, cnt=0 的时候,按理说我们应该得到的掩码是11….1(32个1),但是按照 (1<<cntn)+(~0)
的计算方法得到的却是 000…000(32个0),为了避免这种情况的发生,我们可以在后面再加一个 (((~(!n)) + 1))
,这样当n=0的时候,这个式子的值就是 1....1
; 而当n不等于0的时候,这个式子就等于 0....0
,对最终结果是没有影响的。
1 | /* |
int bitCount(int x)
这题的难度比较高,用语言比较难解释清楚。
首先我们要理解一个比特串中1的个数可以通过什么来计算?
看懂了这张图就可以打开思路,一开始我们可以计算奇数位上的1个数和偶数位上的1的个数,得到一个新的比特串,再求两位中1的个数,4位中1的个数,最后求32位中1的个数。如下图所示:
那么我们怎么来根据这个原理来制作掩码呢?
根据上面这样的计算步骤,扩展到32位,我们需要做5个掩码,分别是
010101....01
, 00110011...0011
,00001111000011110000111100001111
,00000000111111110000000011111111
, 11111111111111111111111111111111
因为我们不能直接使用十六进制来表现这些数,所以我们需要一些帮助比如: 0x55左移;0x33左移;0x0f左移,0xff左移
最后我们利用这些掩码和数字按位与并相加,最终得到比特串中1的个数
1 | /* |
int bang(int x)
这道题也比较简单,是计算非x的值,但是不能使用 !
首先我们让x和其相反数取或来判断x是否等于0,当x不等于0的时候,temp等于1;当x等于0的时候,temp等于0
然后我们让temp算术右移31位,这时候若x不等于0,ans=11…11;若 x为0,ans=0
最后返回ans+1就是答案了
1 | /* |
int tmin(void)
这道题也非常简单,就是求补码的最小值,我们只要得到 100….00这个数就可以了。
1 | /* |
int fitsBits(int x, int n)
这道题要我们判断x是否能被表示为n位数的补码形式
有两个规律: 假设n=4只有当[1xxx]
或[0xxx]
我们才能用n个二进制位来表式x,然后我就将x往右移n-1位~((~n)+1)
,判断剩下的数字是不是全1或者全0就可以了。
1 | /* |
int divpwr2(int x, int n)
这道题要我们计算 $x/(2^n)$ 的值,只要我们了解二进制数除以$2^k$的 原理就能很好的解决。
这里我定义了 flag和 cnt,其中flag是判断这个二进制数是否为负数,若为负数,则为 1…1, 否则为0….0
cnt就是32-k个0和k个1,也就是$(1<<k)-1$
1 | /* |
int negate(int x)
负数就是反码+1,这个题不用过多解释。
1 | /* |
int isPositive(int x)
这道题要我们判断x是否为正数,注意,0和负数都要返回0
首先我们判断这个数是负数还是非负数,若为负数,则返回1;否则返回0
然后我们还要判断这个数是不是等于0
最后将他们的按位或后的结果取反就是答案
1 | /* |
int isLessOrEqual(int x, int y)
这道题说 如果 $x<= y$ 就返回1,否则就返回0
这是有符号数,存在向上向下溢出的情况,我们必须考虑到所有情况。
第一种:x,y同号,且y-x 大于等于0
第二种:x,y 异号,只可能是 x小于0而y大于0
signx和signy是判断x,y是否小于0的,如果小于0,就为1;
samesignflag判断x和y是否同号,若同号则为1,若异号则为0;
1 | /* |
int ilog2(int x)
想法是二分的思想,公式$log(x)=16a+8b+4c+2d+e$。那么count=abcde。因为x长32位,首先我们先将x>>16
,判断高16位是不是还>0,如果>0,!(x>>16)
就是0,我们要将他转换到a的位置就是将! !(x>>16)
再次取非是1,然后<<4
,到a的位置,就说明这个数大于16,1肯定在高16位处,然后在接着将高位折半到8位,就是>>8+16
,看看高8位是不是也是>0
。这样一步步下去就是答案了。
1 | /* |
unsigned float_neg(unsigned uf)
float-neg就是让我们求出浮点数的负数,我们知道浮点数 s,exp,frac 字段分别为 1 位, k=8 位和 n=23 位
给定一个浮点数,我们首先让temp=浮点数左移1位,这样开头8位就都是 exp字段了.
然后,我们必须去除掉NaN的情况,也就是 exp等于11111111
,而后面的 frac字段不为零. 因此我们先让 wu&tmp
是否等于wu,若相等,则位denormalized情况,然后再判断 temp!=wu
如果不等于,就是 NaN;如果等于,就是 Infinity的情况.如果是无穷的,那和普通的浮点数一样只要改变一下符号就可以了,如果是NaN就不用改, 直接返回NaN
1 | /* |
unsigned float_i2f(int x)
这个函数要我们将一个有符号数x转换成浮点数的格式
浮点数的总共由3个部分组成,s,exp和frac
1 | /* |
unsigned float_twice(unsigned uf)
1 | /* |