CSAPP Lab1

lab1

一. 整型代码的实现与思路

该部分主要描述本次实验中整型代码设计思路

1.1 bitXor的代码与实现思路

  • 代码设计如下
/* 
 * bitXor - 只使用 ~ 和 & 实现 x^y
 *   例如: bitXor(4, 5) = 1
 *   合法运算符: ~ &
 *   最多运算符数量: 14
 *   分值: 1
 */
int bitXor(int x, int y)
{
    return ~(~(~x & y) & ~(x & ~y));
}

​ 通过公式可以只用**~以及&**运算符完成该函数。

  • 参考公式如下

x ⊕ y = x ‾ y + x y ‾ x y ‾ = x ‾ + y ‾ \begin{aligned}x\oplus y=\overline{x}y+x\overline{y}\\ \overline{xy}=\overline{x}+\overline{y}\end{aligned} xy=xy+xyxy=x+y

1.2 tmin的代码与实现思路

  • 代码设计如下
/* 
 * tmin - 返回补码表示的最小值 
 *   合法运算符: ! ~ & ^ | + << >>
 *   最多运算符数量: 4
 *   分值: 1
 */
int tmin(void)
{
    return 1 << 31;
}

​ 补码最小值为0x80000000,由于我们只能用到0xFF以下的格式,故将0x01向左移位31个比特位即可的到。

1.3 isTmax的代码与实现思路

  • 代码设计如下
/*
 * isTmax - 如果 x 是补码表示的最大值,则返回 1
 *   合法运算符: ! ~ & ^ | +
 *   最多运算符数量: 10
 *   分值: 1
 */
int isTmax(int x)
{
    return !(~(x + (1 << 31)));
}

​ 补码最大值为0x7FFFFFFF,我们若想该值凑1,可以用该值与0x80000000相加可得到0xFFFFFFFF,该值按位取反并做**!**运算即可返回1,其他情况均可得到0。

1.4 allOddBits的代码与实现思路

  • 代码设计如下
/* 
 * allOddBits - 如果所有奇数位都是 1,则返回 1
 *   位的编号是从 0 (最低有效位 LSB) to 31 (最高有效位 MSB)
 *   例如: allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   合法运算符: ! ~ & ^ | + << >>
 *   最多运算符数量: 12
 *   分值: 2
 */
int allOddBits(int x)
{
    int m = (0xAA << 8) + 0xAA;
    m = (m << 16) + m;

    return !(m ^ (x & m));
}

​ 该函数思路主要需要凑出掩码,用x与掩码或运算来表示奇数位的情况,再与掩码异或运算可得知各个奇数位是否为1,如果不是,那么做**!**判断之后返回0,反之则返回1。

​ 掩码的设计主要通过2次移位运算获得,且第二次移位后需要加上第一次移位后的值。

1.5 negate的代码与实现思路

  • 代码设计如下
/* 
 * negate - 返回 -x 
 *   例如: negate(1) = -1.
 *   合法运算符: ! ~ & ^ | + << >>
 *   最多运算符数量: 5
 *   分值: 2
 */
int negate(int x)
{
    return ~x + 0x01;
}

​ 该部分取反只需返回x的按位取反加1即可。

1.6 isAsciiDigit的代码与实现思路

  • 代码设计如下
/* 
 * isAsciiDigit - 如果 0x30 <= x <= 0x39 ('0' 和 '9'的 ASCII值)则返回 1
 *   例如: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   合法运算符: ! ~ & ^ | + << >>
 *   最多运算符数量: 15
 *   分值: 3
 */
int isAsciiDigit(int x)
{
    return !(0x03 ^ (x >> 4)) & !!((x + 0x06) & 0x10);
}

​ return后面的表达式以**&**左右分为两个部分。

​ 第一个部分的工作主要判断x的第一个字节的高四位是否为0011(3),以及第一个字节以外的字节是否全部为0,这一步主要是为了第二部分只考虑第一字节的低四位的情况。

​ 第二个部分我们只需要保证第一字节的低四位是否为0~9,这里我们只需要加上6判断是否溢出即可。这里由于第一部分已经保证第一字节高四位为0011(3),故溢出一位会使高四位变为0100(4),然后通过简单的**&**运算即可获得答案。

1.7 conditional的代码与实现思路

  • 代码设计如下
/* 
 * conditional - 实现条件运算符 x ? y : z 
 *   例如: conditional(2,4,5) = 4
 *   合法运算符: ! ~ & ^ | + << >>
 *   最多运算符数量: 16
 *   分值: 3
 */
int conditional(int x, int y, int z)
{
    int m = !!x;
    m = ~m + 1;

    return (m & y) + (~m & z);
}

​ 掩码部分我们希望通过x的值使其为0xFFFFFFFF或者0x00000000,这样我们就能通过简单的**&**运算实现返回值。

​ 对于x只需要两次**!**运算即可得到其条件值是0还是1,之后取负可以得到想要的值。

1.8 isLessOrEqual的代码与实现思路

  • 代码设计如下
/* 
 * isLessOrEqual - 如果 x <= y  返回 1, 否则返回 0 
 *   例如: isLessOrEqual(4,5) = 1.
 *   合法运算符: ! ~ & ^ | + << >>
 *   最多运算符数量: 24
 *   分值: 3
 */
int isLessOrEqual(int x, int y)
{
    int yMx = (y + (~x + 0x01)) >> 31;			// yMx 表 y-x
    int s = (x >> 31) ^ (y >> 31);				// s 表 x与y是否符号位相同

    return (!yMx & !s) | !!(s & (x >> 31));
}

​ 参照注释可以很容易理解并得到yMx以及s两个变量,后面只需确保同号时y-x大于0取反(这里取反主要为了使我们可以忽略等于的情况,并且同号时不会发生溢出现象),以及异号时x小于0,这样便可以判断x是否小于等于y

1.9 logicalNeg的代码与实现思路

  • 代码设计如下
/* 
 * logicalNeg - 实现逻辑非 ! 运算符, 使用除 ! 之外的其他允许使用的运算符
 *   例如: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   合法运算符: ~ & ^ | + << >>
 *   最多运算符数量: 12
 *   分值: 4 
 */
int logicalNeg(int x)
{
    return ((x | (~x + 0x01)) >> 31) + 0x1;
}

​ 0除外,x与所有**-x做或运算都不会得到0(这里取x的负值而不是按位取反是因为前者对所有数的运算结果都是1),故x-x**或运算后取符号位加1即可得到想要的值。

1.10 howManyBits的代码与实现思路

  • 代码设计如下
/* howManyBits - 返回补码表示 x 所需要的最少位数(复杂题,选做)
 *  例如: howManyBits(12) = 5
 *            howManyBits(298) = 10
 *            howManyBits(-5) = 4
 *            howManyBits(0)  = 1
 *            howManyBits(-1) = 1
 *            howManyBits(0x80000000) = 32
 *  合法运算符: ! ~ & ^ | + << >>
 *  最多运算符数量: 90
 *  分值: 4
 */
int howManyBits(int x)
{
    int s = x >> 31;
    x = (s & ~x) | (~s & x); // 消除负数,统一为正数

    int m16 = (~(!!(x >> 16)) + 0x01) & 0x10;
    int m8 = (~(!!(x >> (m16 + 0x08))) + 0x01) & 0x08;
    int m4 = (~(!!(x >> (m16 + m8 + 0x04))) + 0x01) & 0x04;
    int m2 = (~(!!(x >> (m16 + m8 + m4 + 0x02))) + 0x01) & 0x02;
    int m1 = (~(!!(x >> (m16 + m8 + m4 + m2 + 0x01))) + 0x01) & 0x01;
    int m0 = (~(!!(x >> (m16 + m8 + m4 + m2))) + 0x01) & 0x01;

    return m16 + m8 + m4 + m2 + m1 + m0 + 1;
}

​ 该问题我们主要探究:对于正数最高位为1的位置是第几个,对于负数而言我们需要判断最高位为0的位置为第几个,当我们第二行代码执行完毕后负数被化为正数,故同样需要寻找最高位为1的位置,最后统一加上符号位即可判断x所需要最少位数。

​ 代码中m****表示以多少数开始判断1在左还是右(例如m16表示我们以该数第16位为界限,第一个1在高16位还是低16位,在高16位则返回16,低16位则返回0),紧接着我们下一个m8**判断高或者低16位的高或者低8位1的位置,依次类推,我们将各m相加以及符号位相加可得到答案。(特别注意m0和m1组合主要为了判断最后两位的组合情况,若为00则返回0,01则返回1,10和11均返回2)

二. 浮点型代码的实现与思路

该部分主要描述本次实验中浮点型代码设计思路

2.1 floatScale2的代码与实现思路

  • 代码设计如下
/* 
 * floatScale2 - 为浮点型参数 f 返回与表达式 2*f 等价的位级表示
 *   参数和返回值都以 unsigned int 传递, 但是它们将被解释成位级表示的单精度浮点值
 *   当参数是 NaN, 返回参数
 *   合法运算符: 任何 int/unsigned 支持的运算符包括 || && 还有 if, while
 *   最多运算符数量: 30
 *   分值: 4
 */
unsigned floatScale2(unsigned uf)
{
    int e = (uf & 0x7F800000) >> 23;
    int s = uf & (1 << 31);

    if (e == 0x00)
        return uf << 1 | s;
    if (e == 0xFF)
        return uf;

    if (e++ == 0xFF)
        return (0x7F800000 | s);

    return (e << 23) | (uf & 0x807FFFFF);
}

​ 该问题主要将溢出与非规格数思考清楚即可。

​ 第一个if主要对非规格数进行返回,此时只需要简单的移位一次并加上符号位即可获得。第二个if主要对溢出进行返回,依据题目返回本身即可。第三个if主要对e做自加判断位移一次是否溢出,若等于0xFF则返回最大值。最后一个return只需要简单的将阶码(已经自加过)拼回去即可。

2.2 floatFloat2Int的代码与实现思路

  • 代码设计如下
/* 
 * floatFloat2Int - 为浮点型参数 f 返回与表达式 (int)f 等价的位级表示 (复杂题,选做)
 *   参数和返回值都以 unsigned int 传递, 但是它们将被解释成位级表示的单精度浮点值
 *   超出表示范围的(包括 NaN 和 infinity) 应该返回 0x80000000u
 *   合法运算符: 任何 int/unsigned 支持的运算符包括 || && 还有 if, while
 *   最多运算符数量: 30
 *   分值: 4
 */
int floatFloat2Int(unsigned uf)
{
    int e = ((uf & 0x7F800000) >> 23) - 0x7F;
    int f = ((uf & 0x007FFFFF) | 0x00800000);

    if (!(uf & 0x7FFFFFFF) || e < 0)
        return 0;
    if (e > 31)
        return 0x80000000;

    if (e > 23)
        f = f << (e - 23);
    else
        f = f >> (23 - e);

    if (!(uf >> 31))
        return f;
    else
        return ~f + 1;

    return 1;
}

​ 首先取出阶码和尾码的值。第一个if判断是否为0或者非规格数,这两类可以返回0。第二个if判断是否越界,越界则按题目要求返回即可。第三个if判断尾码左移或右移。通过上面的几个if基本包括了所有情况。最后通过判断符号位返回相应值。

2.3 floatFloat2Int的代码与实现思路

  • 代码设计如下
/* 
 * floatPower2 - 为任意的 32位 int x 返回与表达式 2.0^x (2.0 的 x 次幂) 等价的位级表示
 *   返回的 unsigned 值应该有和单精度浮点值 2.0^x 一模一样的位级表示
 *   如果结果太小无法表示成 denorm 时,则返回 0
 *   如果太大,则返回 +INF.
 * 
 *   合法运算符: 任何 int/unsigned 支持的运算符包括 || && 还有 if, while
 *   最多运算符数量: 30 
 *   分值: 4
 */
unsigned floatPower2(int x)
{
    int INF = 0xFF << 23;
    int e = x + 0x7F;
    if (e <= 0)
        return 0;
    if (e >= 0xFF)
        return INF;
    return e << 23;
}

​ 首先求得INF并求阶码。两个if只需简单的依照题目要求返回即可,最后将阶码左移返回得到答案。

三. 代码测试部分

该部分主要展示代码测试截图

  • ​ 代码测试输出
root@Nava9:~/Linux_lab/lab1# ./btest
Score   Rating  Errors  Function
 1      1       0       bitXor
 1      1       0       tmin
 1      1       0       isTmax
 2      2       0       allOddBits
 2      2       0       negate
 3      3       0       isAsciiDigit
 3      3       0       conditional
 3      3       0       isLessOrEqual
 4      4       0       logicalNeg
 4      4       0       howManyBits
 4      4       0       floatScale2
 4      4       0       floatFloat2Int
 4      4       0       floatPower2
Total points: 36/36
  • 2
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值