CSAPP Experimento 1: Operación de bit (Laboratorio de datos)

       Esta serie de artículos es un experimento organizado por el curso básico "Sistema Informático" de la carrera de informática de la Universidad de Ciencia y Tecnología de China. Los materiales didácticos y contenidos utilizados en la clase son el libro negro CSAPP. En ese momento, tomó mucha energía y desvíos. Ahora resumiré cada experimento. Este artículo es el primero. Un experimento: manipulación de bits (DataLab).

1. Nombre del experimento: operación de bits

2. Horas experimentales: 3

El contenido y propósito del experimento.

       1. Comprender cómo se representan varios tipos de datos en las computadoras

       2. Domine la representación y el funcionamiento a nivel de bits de los tipos de datos del lenguaje C

       3. Este laboratorio incluye un total de 15 preguntas de programación sobre manipulación de bits

       4. Tu objetivo es resolver estos 15 problemas de programación

       5. Todo el código a implementar está en bits.c

4. Principio experimental

       1. <<: El desplazamiento a la izquierda es: descartar el bit más alto, 0 complementar el bit más bajo (este es el caso de la aritmética y la lógica)

       2. >>: Movámonos a la derecha. Si entiendes la razón para moverte a la izquierda, entonces moverte a la derecha es más fácil de entender.

       El concepto de desplazamiento a la derecha es lo opuesto al desplazamiento a la izquierda, es decir, mover unos bits hacia la derecha, el operador es >>.

       El desplazamiento a la derecha maneja el bit de signo de forma diferente al desplazamiento a la izquierda. Para los enteros con signo, como los tipos int, el desplazamiento a la derecha mantendrá el bit de signo sin cambios, por ejemplo:        

int i = 0x80000000;

i = i >> 1;    //i的值不会变成0x40000000,而会变成0xc0000000

       Es decir, después de mover el bit de signo hacia la derecha, se agrega 0 para números positivos y 1 para números negativos, que es el desplazamiento aritmético a la derecha en lenguaje ensamblador. Además, cuando el número de bits desplazados excede la longitud del tipo, se toma el resto y luego se desplaza el resto.

       El número negativo 10100110 >> 5 (asumiendo que la longitud de la palabra es de 8 bits), entonces el resultado es 11111101

       En resumen, en C, el desplazamiento a la izquierda es un desplazamiento a la izquierda lógico/aritmético (ambos son exactamente iguales) y el desplazamiento a la derecha es un desplazamiento aritmético a la derecha, lo que mantendrá el bit de signo sin cambios. multiplicación rápida según la situación / operación de división, que es mucho más eficiente que el bucle.

       3. !: Lógico NO

       4. ˜ : poco no

       5. & : AND bit a bit

       6. ˆ: XOR

       7. | : bit a bit O

5. Pasos y resultados experimentales

       1.  código de descompresión tar xvf datalab-handout.tar, incluidos los siguientes archivos

       Bits.c: el único archivo que necesita ser modificado

       Btest.c: la función de este archivo es evaluar la corrección de la función bits.c que implementamos,

       LÉAME: Algunas notas sobre btest.

       Dlc: revisión de gramática

       2.  Abra el archivo bits.c, que contiene un equipo de estructura, primero debemos complementar los datos en el equipo

      3.  Bits.c contiene 15 funciones que deben implementarse, y el archivo especifica los operadores lógicos y aritméticos (número especificado) necesarios para implementar cada función.

       ¡Utilice sólo los operadores especificados! ˜ & ˆ | + << >>

       No puede usar bucles o declaraciones condicionales

       No se pueden utilizar constantes con más de 8 bits (ff)

       4.  Después de completar, use ./dlc bits.c para verificar si la sintaxis de bits.c es correcta, es decir, si la cantidad especificada de operadores se usa según sea necesario.

       5.  Use la terminal, ejecute make clean && make btest para compilar el archivo, use la terminal, ejecute ./btest , verifique si lo hizo bien y finalmente ejecute ./driver.pl para obtener el puntaje

       6. Ideas experimentales y código de programa (las ideas están escritas en los comentarios):

/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 1
 * 只使用两种位运算实现异或操作。
 * 
  异或,相同为0,相异为1。相同的话,不是11就是00。
  x & y;就是找出11,(~x) & (~y);就是找出00。
  那么正确答案就是,既不是11,也不是00
 */
int bitXor(int x, int y)
{
  return ~(~x & ~y) & ~(x & y);
}
/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 * 使用位运算获取对2补码的最小 int 值
 * 
   Tmin就是最小的那个负数值,比较特殊,因为没有和它互为相反的正值。
   补码是 0x80000000, 所以直接对1进行左移就好了
 */
int tmin(void)
{
  return 0x1 << 31;
}
//2
/*
 * isTmax - returns 1 if x is the maximum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 * 通过位运算计算是否是补码最大值
 * 
   x判断是否为补码最大值,那么就需要经过某种特定的步骤把它转化为零。
   我们发现最大值+最小值=一个特殊的值,即0xFFFF FFFF,用二进制表示它会是全一,取反之后就是全零
 */

int isTmax(int x)
{
  int i = x + 1; // 这里取极端情况,当x就是最大值的时候,加一就变为最小值
  x = x + i;     // 最大值+最小值得到全一,0xFFFF FFFF
  x = ~x;        // 取反得全零。0x0000 0000
  // 然而到这里还没有结束,发现0xFFFF FFFF,也是特殊情况之一,它加一等于全零了,而他本身是全一的情况,所以我们还得排除这个0xFFFF FFFF。到此我们的答案就出来了~
  i = !i;    // 给i取反,一个非零的布尔值都是true,也就是1
  x = x + i; // 所以当给x+i的时候,要么加0要么就加1,它就会影响到下面的结果
  return !x; // 得到的结果是相反的,还得!一下
}
/* 
 * 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
 * 判断所有奇数位是否都为1,这里的奇数指的是位的阶级是2的几次幂。重在思考转换规律,如何转换为对应的布尔值。
 * 
   这个题目还是比较简单的,采用掩码方式解决。首先要构造掩码,使用移位运算符构造出奇数位全1的数 mask ,0xAA代表的是1010 1010,所以我们将8个A拼起来就行了,即0xAAAAAAAA
   然后获取输入 x 值的奇数位,其他位清零(mask&x),即x=x&0xAAAAAAAA,将这个数偶数位为变为0
   然后与 mask 进行异或操作,若相同则最终结果为0,然后返回其值的逻辑非。

 */
int allOddBits(int x)
{
  int mask = 0xAA | 0xAA << 8;
  mask = mask | mask << 16;
  x = x & mask;
  return !(mask ^ x);
}
/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 * 
 * 不使用 - 操作符,求 -x 值。这个题目是常识
 * 
 */
int negate(int x)
{
  return ~x + 1;
}
//3
/* 
 * 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
 * 
 * 计算输入值是否是数字 0-9 的 ASCII 值
 * 
   x需要>=’0’且<=’9’,将x与临界点作差,然后判断符号位的为0还是1即可
   48 - 57为ascii码数字部分,右边用58是因为0的符号位算正数,所以要多减一次
 */
int isAsciiDigit(int x)
{
  return (!((x + ~48 + 1) >> 31)) & !!((x + ~58 + 1) >> 31);
}

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 * 
 * 使用位级运算实现C语言中的 x?y:z 三目运算符
 * 
   如果根据 x 的布尔值转换为全0或全1更容易解决了,即 x==0 时位表示是全0的, x!=0 时位表示是全1的。
   这就是1-2行代码,通过获取其布尔值0或1,然后求其补码(0的补码是本身,位表示全0;1的补码是-1,位表示全1)得到想要的结果。
   然后通过位运算获取最终值。
 */
int conditional(int x, int y, int z)
{
  x = !!x;
  x = ~x + 1;
  return (x & y) | (~x & z);
}
/* 
 * 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 negX = ~x + 1;                                            //-x
  int addX = negX + y;                                          //y-x
  int checkSign = addX >> 31 & 1;                               //y-x的符号
  int leftBit = 1 << 31;                                        //最大位为1的32位有符号数
  int xLeft = x & leftBit;                                      //x的符号
  int yLeft = y & leftBit;                                      //y的符号
  int bitXor = xLeft ^ yLeft;                                   //x和y符号相同标志位,相同为0不同为1
  bitXor = (bitXor >> 31) & 1;                                  //符号相同标志位格式化为0或1
  return ((!bitXor) & (!checkSign)) | (bitXor & (xLeft >> 31)); //返回1有两种情况:符号相同标志位为0(相同)位与 y-x 的符号为0(y-x>=0)结果为1;符号相同标志位为1(不同)位与x的符号位为1(x<0)
}
//4
/* 
 * 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 
 * 使用位级运算求逻辑非 !
 * 
   逻辑非就是非0为1,非非0为0。利用其补码(取反加一)的性质,除了0和最小数(符号位为1,其余为0),外其他数都是互为相反数关系(符号位取位或为1)。
   0和最小数的补码是本身,不过0的符号位与其补码符号位位或为0,最小数的为1。利用这一点得到解决方法。
 */
int logicalNeg(int x)
{
  return ((x | (~x + 1)) >> 31) + 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)
{
  int b16, b8, b4, b2, b1, b0;
  int sign = x >> 31;
  x = (sign & ~x) | (~sign & x); //如果x为正则不变,否则按位取反(这样好找最高位为1的,原来是最高位为0的,这样也将符号位去掉了)

  // 不断缩小范围
  b16 = !!(x >> 16) << 4; //高十六位是否有1
  x = x >> b16;           //如果有(至少需要16位),则将原数右移16位
  b8 = !!(x >> 8) << 3;   //剩余位高8位是否有1
  x = x >> b8;            //如果有(至少需要16+8=24位),则右移8位
  b4 = !!(x >> 4) << 2;   //同理
  x = x >> b4;
  b2 = !!(x >> 2) << 1;
  x = x >> b2;
  b1 = !!(x >> 1);
  x = x >> b1;
  b0 = x;
  return b16 + b8 + b4 + b2 + b1 + b0 + 1; //+1表示加上符号位
}

//float
/* 
 * 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
 * 求2乘一个浮点数
 * 
   1.若原数为非规格化小数或0时,处理小数部分
 * if(exp_ == 0) return (uf<<1)|s_;
 * 2.若为NaN或INF时
 * if(exp_ == 255) return uf;
 * 直接返回。
 * 3.若为其他情况,即指数加一
 * ++exp_,
 * 4.若加了之后为INF时,保证其不为NaN,即小数部分全为0,
 * if(exp_ == 255) return 0x7f800000|s_;
 * 5.最后为一般情况,直接输出2*f
 * return (uf&0x807fffff)|(exp_<<23);
 */
unsigned floatScale2(unsigned uf)
{

  int exp_ = (uf & 0x7f800000) >> 23;
  int s_ = uf & 0x80000000;
  if (exp_ == 0)
    return (uf << 1) | s_;
  if (exp_ == 255)
    return uf;
  ++exp_;
  if (exp_ == 255)
    return 0x7f800000 | s_;
  return (uf & 0x807fffff) | (exp_ << 23);
}
/* 
 * 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
 * 
 * 将浮点数转换为有符号整数,float_f2i
 * 
 
 * 思路:
 * 先将浮点数分成三段,
 * 符号部分s_ = uf>>31,
 * 指数大小exp_ = ((uf&0x7f800000)>>23)-127,
 * 获取小数部分,并补上浮点数缺省的1,
 * frac_ = (uf&0x007fffff)|0x00800000。
 * 处理特殊情况:
 * 若全为0是返回0,
 * 若指数大于31,整数无法表示溢出返回0x80000000。
 * 若指数小于0,该数0<x<1返回0
 * 若指数部分大于23则将小数部分向左移动frac_ <<= (exp_ - 23) ,exp_代表指数大小。
 * 若指数部分小于23则将小数部分向右移动frac_ >>= (23 - exp_) ,exp_代表指数大小。
 * 考虑最后符号,正数转换为负数不会产生溢出。
 * 若frac_为正数,则根据s_调整正负输出即可。
 * 若frac_为负数,唯一正确情况为0x80000000。


 */
int floatFloat2Int(unsigned uf)
{

  int s_ = uf >> 31;
  int exp_ = ((uf & 0x7f800000) >> 23) - 127;
  int frac_ = (uf & 0x007fffff) | 0x00800000;
  if (!(uf & 0x7fffffff))
    return 0;

  if (exp_ > 31)
    return 0x80000000;
  if (exp_ < 0)
    return 0;

  if (exp_ > 23)
    frac_ <<= (exp_ - 23);
  else
    frac_ >>= (23 - exp_);

  if (!((frac_ >> 31) ^ s_))
    return frac_;
  else if (frac_ >> 31)
    return 0x80000000;
  else
    return ~frac_ + 1;
}
/* 
 * 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
 * 
 * 计算浮点数2.0^x
 

 * 一般情况下,
 * 把x当成浮点数的阶码就可以了,加 127 得到指数阶码,由于小数点后面都是 0,只需左移指数部分
 * 考虑特殊情况,
 * 指数exp<-127和 exp>128,分别返回0和0x7f800000
 */
unsigned floatPower2(int x)
{
  if (x < -127)
    return 0;
  if (x > 128)
    return 0x7f800000;
  x += 127;
  x = x << 23;
  return x;
}

6. Resultados de ejecutar el programa de prueba

Supongo que te gusta

Origin blog.csdn.net/qq_35739903/article/details/119599979
Recomendado
Clasificación