[CSAPP]Data-Lab 深入理解计算机系统位操作-详细解析

前言

 本来想写点什么。

但是想了想。

感觉好像没什么好写的。

函数分析

1. bitXor函数

函数要求

函数名bitXor
参数int , int
功能实现x^y
要求

最大操作数:14,只能使用 ~ 和 & 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。 

/*

 * bitXor - x^y using only ~ and &

 *   Example: bitXor(4, 5) = 1

 *   Legal ops: ~ &

 *   Max ops: 14

 *   Rating: 1

 */

实现分析

根据布尔代数恒等式 x^y = (x & ~y) | (~x & y),使用De Morgan定律将之展开,可得到 x^y = ~ (~x & ~y) & ~ (x & y)

即得到仅使用位运算符 ~ 和 & 实现 x^y 的表达式。

函数实现

int bitXor(int x, int y) {

  return ~(~x & ~y) & ~(x & y);

}

2. getByte函数

函数要求

函数名getByte
参数int , int
功能实现从字x中取出第n个字节
要求

最大操作数:6,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

 /*

 * getByte - Extract byte n from word x

 *   Bytes numbered from 0 (least significant) to 3 (most significant)

 *   Examples: getByte(0x12345678,1) = 0x56

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 6

 *   Rating: 2

 */

实现分析

字节长度固定为8位。取出第n个字节即保留 第n位 ~ 第n+7位,可以使用>>操作符将第n位右移至第1位,使得须保留的变为 第0位~第7位,然后使用掩码 0xFF 消除除低8位之外的所有位。

注意因为保留第n个字节,所以右移操作的实际移位数应为 n*8 即 n>>3

函数实现

int getByte(int x, int n) {

  return x >> (n << 3) & 0xFF;

}

3. logicalShift函数

函数要求

函数名logicalShift
参数int , int
功能实现逻辑右移
要求

最大操作数:20,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * logicalShift - shift x to the right by n, using a logical shift

 *   Can assume that 0 <= n <= 31

 *   Examples: logicalShift(0x87654321,4) = 0x08765432

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 20

 *   Rating: 3

 */

实现分析

在C语言中,整型类型的常数以二进制补码存在,在使用 >> 操作符进行右移操作时,对于int会进行算术右移(即在进行右移时会自动填充二进制补码表示的最高位的值)。

若希望实现逻辑右移(即在右移时自动填充0),可以在算术右移的基础上使用掩码将自动填充的数值置0。

先得到算术位移n位之后的结果 x >> n。

将0x80000000(即1<<31)同样右移n位,使得此掩码的高n+1位均为1,其余位均为0;然后在左移1位,使得掩码此时高n位均为1,其余位均为0;再进行按位取反,得到高n位均为0,其余位均为1的掩码。

将此掩码与 x>>n 做与操作,从而达成将 x>>n 得到的结果的高n位置0而其余位保持不变(即将符号位之上的位清零)的效果。

从而实现将x逻辑右移n位。

函数实现

int logicalShift(int x, int n) {

  return (x >> n) & ~(1 << 31 >> n << 1);

}

4. bitCount函数

函数要求

函数名bitCount
参数int
功能实现计算x中1的数目
要求

最大操作数:40,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * bitCount - returns count of number of 1's in word

 *   Examples: bitCount(5) = 2, bitCount(7) = 3

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 40

 *   Rating: 4

 */

实现分析

由于最大操作数为40,且不允许使用循环操作,所以依次检测32位显然会超出操作数要求。

于是自然想到使用分而治之的方式。

1. 可以使用二分法

  • x = (x & mask) + ((x >> n) & mask);
  • 使用掩码mask将x分为每n位一组,计算每组中1的位数,将计数结果保存在每组的较低n/2位中(n=2、4、8、16),然后将之相加以获得总位数。

2. 也可以将32位分为4组,每次检测8位,然后将检测的结果累加。

  • 也即如下代码所示。
  • 使用掩码 0x01010101 进行与运算:cnt += tmp & (x >> n); (n=0、1、2、3、4、5、6、7),从而计算每个字节中1的个数。
  • cnt = cnt + (cnt >> 16); 将cnt的低16位和高16为相加并将结果存储在cnt中以处理x中可能存在的进位,确保cnt中包含的是整个32位中1的个数。cnt = cnt + (cnt >> 8); 同样用于处理可能得进位。
  • 最后返回cnt的低8位,即为x中所有位中的1的个数。

函数实现

int bitCount(int x) {

  int tmp = (0x01 | (0x01 << 8) | (0x01 << 16) | (0x1 << 24));

  int cnt = (tmp & x) + (tmp & (x >> 1)) + (tmp & (x >> 2)) + (tmp & (x >> 3)) + (tmp & (x >> 4)) + (tmp & (x >> 5)) + (tmp & (x >> 6)) + (tmp & (x >> 7));



  cnt = cnt + (cnt >> 16);

  cnt = cnt + (cnt >> 8);



  return cnt & 0xFF;

}

5. conditional函数

函数要求

函数名conditional
参数int , int
功能实现x?y:z
要求

最大操作数:16,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * conditional - same as x ? y : z

 *   Example: conditional(2,4,5) = 4

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 16

 *   Rating: 3

 */

实现分析

x?y:z 即当x为真时返回y,当x为假时返回z

使用 judge=!!x 对x的值进行判断,当x为真(即x非0)时,judge=1,当x为假(即x为0)时,judge=0。

使用judge构造掩码 mask1 = judge << 31 >> 31 和 mask2 = (!judge) << 31 >> 31。则:

1. 当judge为1时。

  • mask1 = 0xFFFFFFFF, mask2 = 0,进行与操作和或操作后,返回y;

2. 当judge为0时。

  • mask1 = 0, mask2 = 0xFFFFFFFF,进行与操作和或操作后,返回z。

从而实现x?y:z的效果。

函数实现

int conditional(int x, int y, int z) {

  int judge = !!x;

  return ((judge << 31 >> 31) & y) | (((!judge) << 31 >> 31) & z);

}

6. tmin函数

函数要求

函数名tmin
参数-
功能实现返回最小的补码
要求

最大操作数:4,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * tmin - return minimum two's complement integer

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 4

 *   Rating: 1

 */

实现分析

tmin为 0x80000000 即 1 << 31

函数实现

int tmin(void) {

  return 1 << 31;

}

7. fitsBits函数

函数要求

函数名fitsBits
参数int , int
功能实现判断x的补码能否表示成n位
要求

最大操作数:15,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * fitsBits - return 1 if x can be represented as an

 *  n-bit, two's complement integer.

 *   1 <= n <= 32

 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 15

 *   Rating: 2

 */

实现分析

mov = 32 + (~n + 1) = 33 + ~n 计算位数差值32-n,即32位整型中剩余的位数。

使用 x << mov >> mov 将x先左移mov位再右移mov位,以检查是否丢失高位信息,再与x进行比较,如果x经左移和右移后仍与原始值相同,则说明未丢失高位信息,使用n位可以表示x的补码。

函数实现

int fitsBits(int x, int n) {

  int mov = 33 + ~n;

  return !((x << mov >> mov) ^ x);

}

8. dividePower2函数

函数要求

函数名dividePower2
参数int , int
功能实现计算 x/(2^n)
要求

最大操作数:15,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * dividePower2 - Compute x/(2^n), for 0 <= n <= 30

 *  Round toward zero

 *   Examples: dividePower2(15,1) = 7, dividePower2(-33,4) = -2

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 15

 *   Rating: 2

 */

实现分析

计算 x/(2^n),即将x进行右移n位。但是有符号数除以2的整数次幂的上,x右移n位等于整除 2^n并向下舍入,而当x<0时是向错误的方向舍入(更负的溢出),所以此时应使用偏置 (1 << n) + (~1 + 1) 以实现向上取整

也即:

  1.  当 x>=0 时(即符号位 x>>31 为0时),使用向下取整,直接右移;
  2.  当 x<0 时(即符号位 x>>31 为1时),使用偏置进行向上取整。

函数实现

int dividePower2(int x, int n) {

    return (x + (((1 << n) + (~1 + 1)) & (x >> 31))) >> n;

}

9. negate函数

函数要求

函数名negate
参数int
功能实现-x
要求不使用负号。最大操作数:5,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。直接使用的常数需位于0~0xFF范围内。

/*

 * negate - return -x

 *   Example: negate(1) = -1.

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 5

 *   Rating: 2

 */

实现分析

逻辑上,从二进制到有符号数有如下公式:

B2T(X)=-x_{w-1}·2^{w-1}+\sum_{i=0}^{w-2}x_i·2^i

假设x为负数,y为其绝对值(y为无符号数),由于非负数不需要变换,有x=-y,即:

-2^{w-1}+\sum_{i=0}^{w-2}x_i·2^i=-\sum_{i=0}^{w-2}y_i·2^i

从而得到:

\sum_{i=0}^{w-2}x_i·2^i=2^{w-1}-\sum_{i=0}^{w-2}y_i·2^i=\sum_{i=0}^{w-2}(1-y_i)·2^i+1

其中:

2^{w-1}=\sum_{i=0}^{w-2}2^i+1

x_{w-1}=1

y_{w-1}=0

由所得结果 \sum_{i=0}^{w-2}x_i·2^i==\sum_{i=0}^{w-2}(1-y_i)·2^i+1 可知:负数可以通过对应的无符号数取反后加1实现到二进制补码的转换。

即 -x = ~x + 1

函数实现

int negate(int x) {

  return ~x + 1;

}

10. howManyBits函数

函数要求

函数名howManyBits
参数int
功能实现计算表达x所需的最少位数
要求

最大操作数:90,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/* 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

 */

实现分析

由于最大操作数为90,且不允许使用循环操作,所以依次检测32位显然会超出操作数要求。

于是自然想到使用分而治之的方式,使用二分法,分别检测16位、8位、4位、2位、1位中是否有1的存在,从而逐步逼近,最终得到表达x所需最少的位数。

首先得到x的符号 sign = x >> 31

在本题中可以通过掩码左移x右移两种方式实现检测。以下代码使用掩码左移的方式。

  1. cnt 为表达x所需的最少位数;
  2. tmp 为掩码;
  3.  judge 判断当前检测的n(n=16、8、4、2、1)位中是否有1的存在。若有,则judge为1*n,若无,则judge为0。
  4.  special 判断x是否为0或-1的特殊情况。

因为在进行右移操作时,有符号数为算术右移,自动填补符号位的值,若x为负数则填充1,会对judge的判断造成影响,所以将负数取反 x = sign ^ x(等价于~x)。

tmp初值为 0xFFFF0000

使用 x & tmp 判断 高16位 中是否有1存在:

此方式将问题缩小范围,从求解32位数所需要的实际最少位数,变成求解16位数所需要的实际最少位数+16或0。以此方式依次向下查找。

1. 若有1存在:

  • 则x一定使用低16位,即至少需要16位来表示,所以judge = 16,cnt += 16;

2. 若无1存在:

  • 则x最多使用16位来表示,judge = 0,cnt += 0。

根据cnt的值判断下一步应该判断 高16位中的高8位(tmp = 0xFF000000) 还是 低16位中的高8位(tmp = 0x0000FF00)中是否有1存在。

使用 x & tmp 判断 高8位 中是否有1存在:

1. 若有1存在:

  • 则x一定使用低8位,即至少需要cnt+8位来表示,所以judge = 8,cnt += 8;

2. 若无1存在:

  • 则x最多使用cnt+8位来表示,judge = 0,cnt += 0。

根据cnt的值判断下一步应该判断 高16位中的高8位中的高4位(tmp = 0xF0000000)高16位中的低8位中的高4位(tmp = 0x00F00000)低16位中的高8位中的高4位(tmp = 0x0000F000)还是 低8位中低8位的高4位(tmp = 0x000000F0) 中是否有1存在。

使用 x & tmp 判断 高4位 中是否有1存在:

1. 若有1存在:

  • 则x一定使用低4位,即至少需要cnt+4位来表示,所以judge = 4,cnt += 4;

2. 若无1存在:

  • 则x最多使用cnt+4位来表示,judge = 0,cnt += 0。

n=2和n=1的情况以此类推。最终得到的cnt是 最大权重的bit所在位,所以最后答案应该加2,但是 特殊情况0和-1则应该加1

分类考虑 cnt + (1 << (!special)) 从而得到最终的结果。

函数实现

int howManyBits(int x) {

  int sign = x >> 31;

  int cnt = 0;

  int tmp;

  int judge;

  int special = !x | !(x ^ (~1 + 1));

  x = sign ^ x;



  tmp = (0xFF | (0xFF << 8)) << 16;

  judge = !!(x & tmp) << 4;

  cnt += judge;



  tmp = 0xFF << (cnt + 8);

  judge = (!!(x & tmp)) << 3;

  cnt += judge;



  tmp = 0xF << (cnt + 4);

  judge = (!!(x & tmp)) << 2;

  cnt += judge;



  tmp = 0x3 << (cnt + 2);

  judge = (!!(x & tmp)) << 1;

  cnt += judge;



  tmp = 0x1 << (cnt + 1);

  judge = !!(x & tmp);

  cnt += judge;



  return cnt + (1 << (!special));

}

11. isLessOrEqual函数

函数要求

函数名

isLessOrEqual

参数int , int
功能实现判断x<=y是否成立
要求

最大操作数:24,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内

/*

 * isLessOrEqual - if x <= y  then return 1, else return 0

 *   Example: isLessOrEqual(4,5) = 1.

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 24

 *   Rating: 3

 */

实现分析

按情况分类,将情况可分为四种:xy的符号位均为1、xy的符号位均为0、x符号位为0且y的符号位为1、x的符号位为1且y的符号位为0。

sign_x = x >> 31 判断x的符号位,sign_y = y >> 31 判断y的符号位。

通过判断x、y差值的符号位来判断当x和y符号相同时,x、y的大小关系。因为当差值为正或0时,其符号位均为0,而当其为负时,符号位为1,所以 使用 y-x 来代替 x-y 进行差值的符号判断以减少工作量。

差值的符号位判断 diff = !((y + (~x + 1)) >> 31),当差值为正或0时,diff = 1;当差值为负时,diff = 0。

1. xy的符号位均为1

如果x为Tmin(即 0x80000000)时,为32位整型所能表示的最小负数,必然会小于等于y。其他情况下,diff为1时,x<=y。

2. xy的符号位均为0

diff为1时,x<=y。

3. x符号位为0且y的符号位为1

x必然大于y。

4. x的符号位为1且y的符号位为0

x必然小于y。

函数实现

int isLessOrEqual(int x, int y) {

  int sign_x = x >> 31;

  int sign_y = y >> 31;

  int Tmin = 1 << 31;

  int diff = !((y + (~x + 1)) >> 31);



  return (sign_x & !sign_y) | (!sign_x & !sign_y & diff) | (sign_x & sign_y & (!(x ^ Tmin) | diff));

}

12. intLog2函数

函数要求

函数名intLog2
参数int
功能实现计算⌊log2(x)⌋(向下取整)
要求

最大操作数:90,只能使用 ! ~ & ^ | + << >> 运算符,将结果返回。

直接使用的常数需位于0~0xFF范围内。

/*

 * intLog2 - return floor(log base 2 of x), where x > 0

 *   Example: intLog2(16) = 4

 *   Legal ops: ! ~ & ^ | + << >>

 *   Max ops: 90

 *   Rating: 4

 */

实现分析

和10. howManyBits函数的实现方法相同,是该函数的简化版本。因为intLog2函数 不需要考虑0和负数的情况,可以直接使用x进行判断。又因为是向下取整,所以判断得到的最大权重的bit所在位即为所求。

在本题中也可以使用掩码左移x右移两种方式进行计算。以下代码中使用x右移的方式。

  1. cnt 为表达x所需的最少位数;
  2. tmp 为x右移后结果;
  3. judge 判断当前检测的n(n=16、8、4、2、1)位中是否有1的存在。所使用判断的掩码依次分别为0xFFFF、0xFF、0xF、0x3、0x1。若有,则judge为1*n,若无,则judge为0。

tmp初值为 x >> 16

使用 0xFFFF & tmp 判断 高16位 中是否有1存在:

此方式将问题缩小范围,从求解32位数所需要的实际最少位数,变成求解16位数所需要的实际最少位数+16或0。以此方式依次向下查找。

1. 若有1存在:

  • 则x一定使用低16位,即至少需要16位来表示,所以judge = 16,cnt += 16;

2. 若无1存在:

  • 则x最多使用16位来表示,judge = 0,cnt += 0。

根据judge的值判断下一步应该判断 高16位中的高8位(tmp = x >> 16 >> 8)还是 低16位中的高8位(tmp = x >> 0 >> 8)中是否有1存在。

使用 0xFF & tmp 判断 高8位 中是否有1存在:

1. 若有1存在:

  • 则x一定使用低8位,即至少需要cnt+8位来表示,所以judge = 8,cnt += 8;

2. 若无1存在:

  • 则x最多使用cnt+8位来表示,judge = 0,cnt += 0。

根据judge的值判断下一步应该判断 高16位中的高8位中的高4位(tmp = x >> 16 >> 8 >> 4)高16位中的低8位中的高4位(tmp = x >> 16 >> 0 >> 4)低16位中的高8位中的高4位(tmp = x >> 0 >> 8 >> 4)还是 低8位中低8位的高4位(tmp = x >> 0 >> 0 >> 4)中是否有1存在。

使用 x & tmp 判断 高4位 中是否有1存在:

1. 若有1存在:

  • 则x一定使用低4位,即至少需要cnt+4位来表示,所以judge = 4,cnt += 4;

2. 若无1存在:

  • 则x最多使用cnt+4位来表示,judge = 0,cnt += 0。

n=2和n=1的情况以此类推。

函数实现

int intLog2(int x) {

  int cnt = 0;

  int tmp;

  int judge;



  tmp = (x >> 16);

  judge = (!!((0xFF | (0xFF << 8)) & tmp)) << 4;

  cnt += judge;

  x = x >> judge;



  tmp = (x >> 8);

  judge = (!!(0xFF & tmp)) << 3;

  cnt += judge;

  x = x >> judge;



  tmp = x >> 4;

  judge = (!!(0xF & tmp)) << 2;

  cnt += judge;

  x = x >> judge;



  tmp = x >> 2;

  judge = (!!(0x3 & tmp)) << 1;

  cnt += judge;

  x = x >> judge;



  tmp = x >> 1;

  judge = 1 << (!!(0x1 & tmp));

  cnt += judge;



  return cnt + (~1 + 1);

}

13. floatAbsVal函数

函数要求

函数名floatAbsVal
参数unsigned
功能实现计算f的绝对值的位级表示
要求

最大操作数:10,可以使用任何整型或无符号数的操作,

包括 || && 和if while等。

/*

 * floatAbsVal - Return bit-level equivalent of absolute value of 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 representations of

 *   single-precision floating point values.

 *   When argument is NaN, return argument..

 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while

 *   Max ops: 10

 *   Rating: 2

 */

实现分析

首先通过左移和右移,抓取8位阶码:exp = (uf << 1) >> 24

!(exp ^ 255) && (uf << 9) 判断是否为特殊值NAN(E = 0xFFFF 且 M != 0),若是则就直接返回。

否则将符号位置0 (uf << 1) >> 1 即可。

函数实现

unsigned floatAbsVal(unsigned uf) {

  unsigned exp = (uf << 1) >> 24;

  if (!(exp ^ 255) && (uf << 9))

    return uf;

  return (uf << 1) >> 1;

}

14. floatScale1d2函数

函数要求

函数名

floatScale1d2

参数unsigned
功能实现计算0.5*f的位级表示
要求

最大操作数:30,可以使用任何整型或无符号数的操作。

包括 || && 和if while等。

/*

 * floatScale1d2 - Return bit-level equivalent of expression 0.5*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

 */

实现分析

首先取出符号位sign和阶码exp。先判断是否是特殊值NAN或INF,若是则直接返回uf,若非则进行下一步规范化判断。

规范化的指数至少为2,否则exp=1或0,此时会考虑 舍入

若E>1,说明是规范化值,直接使用掩码0x807FFFFF屏蔽x的阶码,然后使用移位后的阶码覆盖即可。

反之,说明是不规范的极小值,考虑舍入。

函数实现

unsigned floatScale1d2(unsigned uf) {

  unsigned sign = (uf >> 31) << 31;

  unsigned exp = (uf << 1) >> 24;



  if (!(exp ^ 255))

    return uf;

  if (exp > 1)

  {

    exp = exp - 1;

    return (uf & 0x807FFFFF) | (exp << 23);

  }

  if (!((uf & 3) ^ 3))

      uf = uf + 2;

  return ((uf >> 1) & 0x7FFFFF) | sign;

}

15. floatFloat2Int函数

函数要求

函数名floatFloat2Int
参数unsigned
功能实现计算(int)f的位级表示
要求

最大操作数:30,可以使用任何整型或无符号数的操作。

包括 || && 和if while等。

​​​​​​​ /*

 * 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

 */

实现分析

单精度浮点数uf转换为整数int。

  1. sign = uf >> 31:提取浮点数uf的符号位,存储在sign变量中,其中1表示负数,0表示正数。
  2. exp = (uf << 1) >> 24:提取浮点数uf的指数部分,存储在exp变量中。
  3. frac = ((uf << 9) >> 9) + (1 << 23):提取浮点数uf的尾数部分,存储在frac变量中,同时,加上隐藏的1(即 1 << 23),以将其还原为规格化的浮点数。
  4. bias = 150u - exp:指数的偏置。

使用 (!(exp ^ 255)) 检查特殊情况NaN或INF,返回 0x80000000u。

使用 (!exp) 检查特殊情况浮点数的绝对值小于1,返回 0。

使用 (bias + 7) 检查指数的偏置值是否超出整数范围,若偏置值加7小于0,表示超出整数范围,返回 0x80000000u。

然后根据偏置值计算整数部分,若偏置值为负,表示需要将尾数右移以获得整数部分,若偏置值为正数,表示需要将尾数左移以获得整数部分。

最后,根据符号位来确定整数的正负,如果是负数,将整数取反并加1。

函数实现

int floatFloat2Int(unsigned uf) {

  unsigned sign = uf >> 31;

  unsigned exp = (uf << 1) >> 24;

  unsigned frac = ((uf << 9) >> 9) + (1 << 23);

  int bias = 150u - exp;

  int tmp;



  if (!(exp ^ 255))

    return 0x80000000u;

  if (!exp)

    return 0;



  if ((bias + 7) >> 31)

    return 0x80000000u;

  if (bias >> 31)

    tmp = frac << ((~bias) + 1);

  else if ((32 + (~bias)) >> 31)

    tmp = 0;

  else

    tmp = frac >> bias;



  return (!sign) ? tmp : ((~tmp) + 1);

}

实验总结

在实验过程中着实坎坷,因为在先前在C语言进行程序编写时也少有使用位操作进行运算,对其实是多有忽视,骤然需要严格限定使用位操作进行编写的时候思维方式仍转换不过来,磕磕绊绊不慎流畅。

首先是大部分题目都通过使用巧妙的掩码进行或、与操作进行某些位的置零或者某些位的1判断,刚开始写的时候一直想不到这部分,最初的思路是纯靠左移和右移来进行实现,显然不太能行。后来逐渐开始上手之后就得心应手很多。

像是判断等于的部分起先也很为难,掩码和位操作更加熟练之后就想到可以用异或操作加!来进行判断。

又比如说在bitCount和howManyBit两个函数部分仍然是使用for循环的思维希望能够一位位进行判断,二分法分而治之的思想也是走了不少弯路才试出来的,为了理清楚思路还画了不少流程图,但是后来也可以使用掩码左移和x右移两种方式进行解决。

很多题目都不断的尝试了许多方法,有些行得通,有些不行,有些会更简单,有些则更加复杂,好在虽然过程并非一帆风顺,但最终还是可以说是圆满地完成了实验,实在可以说是边试错边学习。实践果真是检验真理的唯一标准,真正自己亲身去实验,去思考使用底层的位运算来实现函数的时候,才真的对于整型、浮点数、二进制、十六进制、位操作这些等等有了更加深刻,更加深入的印象和理解。


 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 深入理解计算机系统(CSAPP)是由Randal E. Bryant和David R. O'Hallaron编写的经典计算机科学教材。该教材通过涵盖计算机体系结构、机器级别表示和程序执行的概念,帮助学生深入理解计算机系统的底层工作原理和运行机制。 深入理解计算机系统的练习题对于学生巩固并应用所学知识非常有帮助。这些练习题涵盖了计算机硬件、操作系统和编译器等多个领域,旨在培养学生解决实际问题和设计高性能软件的能力。 对于深入理解计算机系统的练习题,关键是通过实践进行学习。在解答练习题时,应根据课本提供的相关知识和工具,仔细阅读问题描述,并根据实际需求设计相应的解决方案。 在解答练习题时,需要多角度思考问题。首先,应准确理解题目要求,并设计合适的算法或代码来解决问题。其次,应考虑代码的正确性和效率,以及对系统性能的影响。此外,还要注意处理一些特殊情况和异常情况,避免出现潜在的错误或安全漏洞。 解答练习题的过程中,应注重查阅相关资料和参考优秀的解答。这可以帮助我们扩展对问题的理解,并学习他人的思路和解决方法。同时,还可以通过与同学和老师的讨论,共同探讨问题和学习经验。 总之,通过解答深入理解计算机系统的练习题,可以帮助学生巩固所学知识,同时培养解决实际问题和设计高性能软件的能力。这是一个学以致用的过程,可以加深对计算机系统运行机制和底层工作原理的理解。 ### 回答2: 理解计算机系统(CSAPP)是一本经典的计算机科学教材,通过深入研究计算机系统的各个方面,包括硬件、操作系统和编程环境,对于提高计算机科学专业知识与能力具有很大帮助。 练习题是CSAPP中的重要部分,通过练习题的完成,可以加深对计算机系统的理解,并将理论知识转化为实践能力。练习题的数量、难度逐渐递增,从简单的概念与基础问题到复杂的系统设计与实现。 在解答练习题时,首先需要对题目进行仔细阅读和理解,明确题目的要求和限制条件。然后,可以利用课堂讲解、教材内容、网络资源等进行查阅和学习相应的知识。同时,还可以参考课后习题解答等资料,了解一些常见的解题方法和思路。 在解答练习题时,可以利用计算机系统的工具和环境进行实际测试和验证。例如,可以使用调试器、编译器和模拟器等工具对程序或系统进行分析和测试。这样可以更加深入地理解问题的本质,并找到恰当的解决方法。 另外,解答练习题时还可以与同学、教师和网上社区进行交流和讨论。这样可以互相学习和交流解题思路,共同解决问题。还可以了解不同的解题方法和技巧,提高解题效率和质量。 练习题的解答过程可能会遇到一些困难和挑战,例如理论知识的不足、复杂问题的分析与解决。但是通过不断地思考和实践,相信可以逐渐提高解题能力,更好地理解计算机系统。 总之,深入理解计算机系统(CSAPP)练习题是提高计算机科学专业知识和能力的重要途径。通过仔细阅读和理解题目,查阅相关知识,利用计算机系统工具和环境进行实践,与他人进行交流和讨论,相信可以更好地理解计算机系统的各个方面,并将知识转化为实际能力。 ### 回答3: 《深入理解计算机系统(CSAPP)》是计算机科学领域的经典教材之一,对于深入理解计算机系统的原理、设计和实现起到了极大的帮助。在阅读这本书的过程中,书中的习题也是非常重要的一部分,通过做习题,我们可以更好地理解书中所讲的概念和思想。 CSAPP的习题涵盖了课本中各个章节的内容,从基础的数据表示和处理、程序的机器级表示、优化技术、程序的并发与并行等方面进行了深入探讨。通过解答习题,我们可以对这些知识进行实践应用,巩固自己的理解,并培养自己的解决问题的思维方式。 在解答习题时,我们需要充分理解题目要求和条件,并从知识的角度进行分析。有些习题可能需要进行一些编程实践,我们可以通过编程实现来验证和测试我们的思路和解决方案。在解答问题时,我们还可以查阅一些参考资料和网上资源,充分利用互联网的学习资源。 在解答习题时,我们需要保持积极的思维和态度。可能会遇到一些困难和挑战,但是通过坚持和努力,我们可以克服这些困难,提高我们的解决问题的能力。同时,我们还可以通过与同学或者其他人进行讨论,相互分享解题经验和思路,从而更好地理解问题。 综上所述,通过深入理解计算机系统(CSAPP)的习题,我们可以进一步巩固和深化对计算机系统的理解。掌握这些知识,不仅可以提高我们在计算机领域的能力,还可以为我们未来的学习和职业发展奠定重要的基础。因此,认真对待CSAPP的习题,是我们在学习计算机系统知识中不可或缺的一部分。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值