lab1-datalab
由于我大一寒假时写过logicalneg之前的题,这次实验我直接沿用了之前的bits.c,logicalneg前的函数代码里塞了一些没啥必要的注释。在报告里同样会写出思路,不影响报告格式。
INTEGER 部分:
bitXor:
用~和&实现异或 。
1 0 0 |
可以逆推出异或的前一步是构造出这样的真值表结果,这种三个1一个0的结果通过~和& 很容易构造出来
0111 可以看成或运算 用德摩根定律也可以转化为~和&
int a = ~(x&y); |
tmin:
tmin就是二进制补码数的最小值,直接把1移到最高位就行了
return 1<<31; |
isTmax:
对于Tmax 有个性质是+1会变成Tmin , 他们在位上正好是完全相反的,两者异或可以得到-1,在其他数之中只有-1 也有相同的性质。 只要排除这个情况就行了。 正好-1 + 1又等于0, !(-1+1) 和 !(Tmax +1 ) 是不同的 可以他们整合到最后的判断算式中。
我整合的方法是先计算(x & (x+1) ) ,再计算!(x+1) 只有两者都为0 ,才返回1 用一个加法和一个! 就能实现.
int y=x+1; |
allOddBits:
如果所有奇数位都是1就返回1。这里我想到掩码,构造一个奇数位都是1的数t ,让它和 x 与 ,就能单独取出奇数位 然后和t异或 ,只有和t完全一致,才能得到0 ,!之后就能得到1
int t=(0xAA<<24)+(0xAA<<16)+(0xAA<<8)+(0xAA); |
negate:
返回-x, 很老套的方法? 直接返回~x +1 就行了
return ~x + 1; |
isAsciiDigit:
ascii数字从0x30到0x39, 先观察这些数字的共同特性 , 0x30是0b0110000,0x39 是 0b0111001, 可以发现有很大一部分由0b0110开头 ,并且是他们特有的 ,先判断是不是这样的数据,然后单独处理0x38和0x39。
return !((x>>3)^0x6)|!(x^0x39)|!(x^0x38); |
conditional:
实现条件取值。
if(x) 相当于 if (!!x) , 我的思路是将!x 扩大32位,然后对两者进行掩码,再用|合并,那么就要控制x不为0时 取 全1或全0 ,为0时全0或全1
我利用了-1+1 可以在这两种取值突变的特性实现上述功能。
int t=!x+~0; |
isLessOrEqual
我直接用了个非常笨的方法 ,把一个是正数一个负数的情况直接判断出来。如果两个数符号相同 就 判断 x-y-1 是不是正数或者0 正好-y 是 ~y + 1 还省了一个1
int p=(x>>31)&1; |
logicalNeg:
实现!的功能。我想到了0的一个特殊的特性 -0 = 0; 那么我只要判断 ~x + 1 和原来的数据是否是相等的 ,但是要小心tmin 因为 -tmin仍然是tmin 。 我做这题时用了另外一个判断方法,就是判断是否一正一负。这样可以通过或的特性排除掉tmin全负的情况。
int a = ~x+1; |
howManyBits:
先观察规律,发现对于正数和负数都是一样的: 找到第一个变化的位 ,然后再加上1。
利用算数右移可以根据第一位来选择性的取反,这样就统一成一种情况了x = x^(x>>31);
之后的目标就是找到第一个1的位置 ,最开始我的想法是逐个查找,但是30个位的 + >> ! 就已经超过90了,需要更强大的查找算法。于是我想到了二分查找: 用p对分割进行标记,如果分割左侧有1 ,就缩小范围到p左侧 ,否则到p右侧。
例如p等于32时,分割如下:
0x0000 | 0x0000 |
那么问题就转化成了如何实现在受限情况下更简洁的二分查找。 这让我想到了c语言编译器对if的一种优化 : 转化分支为条件赋值。例如 第一次查找 ,p=32 ,先假设左边没有1 也就是 p = 16; 如果左边有1,再将 p + 32即可,用代码表示就是
p=32; |
同时这么做有个好处 ,假设这一次运行完后p等于48 ,需要先-8 ,就可以用异或实现 ,因为p在16的位置一定是 1, 8 的位置一定是0.
p = (p^24) + ((!!(x>>p))<<4); |
需要特殊处理的是最后在2位的区间内查找,此时不应该进行先-1,因为之后并没有再缩小区间的需求了。
p = p+ ((!!(x>>p))); |
还有一个特殊情况是全0 ,可以当作在第-1位有一个1,我的上述算法并不能判断出这个情况,因此要单独处理
综上,代码为
int p ; |
floatScale2
实现 浮点数*2 。
考虑三种情况 inf, 规格数,0 。
inf时直接返回。
规格数时 令阶码+1
0时 ,根据IEEE规定的特性 假设有0b 0.1111 , 要使其翻倍 ,即变成 0 b 1.1110 ,反映在浮点数中 ,由于规格数 1和 0 ,都是x**-126 , 最终只需要让尾数整体左移
int T; |
floatFloat2Int
总体上分三种情况
exp - 127 >= 31,此时过大 返回0x80000000。
exp - 127 <0 此时过小 返回0。
之后还可以细分成两个情况。
exp - 127 < 23 时 这时候应该让尾数右移,因为尾数最低位是2的-23次方。
exp -127 >=23 时,这时候尾数左移。
另外还要考虑符号位 ,最后根据符号位判断是否取负值
int floatFloat2Int(unsigned uf) { |
C99 开始 规定负数向0截断,这使得在负数情况下 这种直接取反的行为是合理的。
floatPower2
考虑三种情况
过大时直接返回+inf
不过大时 如果>0 只需要依靠阶码,如果<0 ,在<-126 的情况下 还可以用到尾数 ,也就是说最多可以表示到-149
过小时返回0
按照上述几种情况分别实现即可
if (x > 127) return 0x7f800000; |
另外,在浮点数部分有个规则是禁止任何形式的类型转换,我认为这里应该理解为禁止对位级存储规则进行转换,否则这和给出的另一个规则: 可以使用任何integer/unsigned operations 是矛盾的。
btest在最后一个函数卡了很久 不过strace发现题目设置的是alarm(10) , 并没有超出时间,但不知道为何会卡这么久。