CS: app: lab1 – datalab super detailed

Time:2021-12-1

Write in front

Before the postgraduate entrance examination, the book of CSAPP had been brushed 5 or 6 times, so I had a good understanding of book knowledge. When I was learning C + + recently, I brushed the famous CSAPP experiment.

0. Environmental preparation

It is best to prepare a pure Linux system. It is recommended to use docker to build a CentOS or Ubuntu system

Download of experimental data
CS:APP3e, Bryant and O’Hallaron
Please refer to the following article for environment construction on docker
CSAPP: lab0 – carrying environment

Pullcentossystem
docker pull centos

Establish directory mount to realize file synchronization
docker container run -it -v /Users/xxxx/yourFilePath:/csapp --name=csapp_env centos /bin/bash

/Users / XXXX / yourfilepath please replace it with the directory you want to synchronize
: / CSAPP please also replace it with the directory you want to name

The CSAPP directory here is the directory synchronized with your local directory

After synchronization, you can find that the CSAPP directory under docker is synchronized with our yourfilepath file


A result similar to the above indicates that the configuration is correct

  • Configuring the compilation environment
  • Update Yum source
    yum -y update
  • Install sudo
    yum install sudo
  • Installing the C / C + + compilation environment
    yum install make automake gcc gcc-c++ kernel-devel
  • Installing GDB
    yum install gdb
  • Prepare 32-bit embedded C library
    yum install glibc-devel.i686
  • Read readme to complete the configuration
    Here, you need to first enter the mapped file directory CSAPP file, and then refer to the readme file
shell    To compile and run the btest program, type:
unix> make btest
unix> ./btest [optional cmd line args]

After completing the above operations, our configuration is complete.

Next, we can write our code in a native compiler. Then compile and run our code on the virtual container in docer. ✅

Write in compiler

Compile and run in the docker container

The blue arrow is compiled. The red arrow indicates operation

Note: the btest should be recompiled every time the bits. C file is changed. If you need to check the correctness of a single function, you can use the – f flag:

text $ ./btest -f bitXor

DLC program can detect whether we have violations. If there is no output, there is no problem

text $ ./bits.c

Note that every time you close docker, you need to start our CentOS first in the next run.

First find us namedcsapp_envContainer ID of the container

  1. Then the docker start container ID starts our container

  2. Enter the following command to enter the running container

Docker exec - it container ID / bin / Bash

1. Start of experiment

*   IMPORTANT. TO AVOID GRADING SURPRISES:
*   1. Use the dlc compiler to check that your solutions conform
*      to the coding rules.
*   2. Use the BDD checker to formally verify that your solutions produce 
*      the correct answers.
*/

1.1 bitXor

a^b=
1.(a|b)&(~a|~b)
2.~(~a&~b)&~(a&b)
3.(a&~b)|(~a&b)

The XOR operation can be expressed in these three ways. The specific derivation can be performed by myself. Referring to discrete mathematics, I pushed it myself and found that it is not difficult

Mainly the application of De Morgan law

\[

 \begin{align}   A\bigoplus B & = \overline{\overline AB\cup A \overline B} \\             & = \overline{(A \cup \overline B)\cap (\overline A \cup B})  \\             & = ( \overline{(A \cup \overline B)\cap \overline A )\cup ( (A \cup \overline B)\cap B )}\\             & = \overline{(\overline A \overline B) \cup( A B)} \\             & =\overline{(\overline A \overline B)}\cap \overline{( A B)}\\           \end{align}
\]

We can skip this example by selecting the second operation

int bitXor(int x, int y) {
  return ~(~x&~y)&~(x&y);
}

1.2 tmin

int tmin(void) {

  return 1<<31;

}

1.3 tmax

Title Description

* isTmax - returns 1 if x is the maximum, two's complement number,
*     and 0 otherwise 
*   Legal ops: ! ~ & ^ | +
*   Max ops: 10
*   Rating: 1
*/

thinking

We consider the four digit maximumx=0111Then x + 1 becomes1000We are right1000Take non0111It will change back to the x value

If we can use equal here, is it done directly, but not equal? Here we can use a bit operation trick. We know that we and ourselves will get 0, that is, we can use exclusive or to judge equal!((~(x+1)^x))Judge whether this is 1 to judge whether it is the maximum value

There is an exception herex=-1because-1=1111He uses the above formula to judge that it is also consistent, so he wants to make a special judgment -1!!(x+1)This operation – 1 is not the same as the maximum value

int isTmax(int x) {

  return !((~(x+1)^x))&!!(x+1);
}

1.4 allOddBits

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

thinking

A=1010A is a typical number with even bits of 1. It only needs a four bit binary numberX & A = AThis indicates that the binary is qualified. That’s just judgmentx & 0xAAAAAAAA == 0xAAAAAAAAIt’s OK, because it can’t be defined directly0xAAAAAAAAWe need some bit operation tips

int a=0xAA<<8; //0xAA00
int c=a|0xAA; //0xAAAA
int d=c<<16|c; //0xAAAAAAAA

The operation of the equal sign can be used directlya == bEquivalent to!((a & b)^b)

int allOddBits(int x) {
    int a=0xAA<<8;
    int c=a|0xAA;
    int d=c<<16|c;
  return !((x&d)^(d));
}

1.5 negate

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */

thinking

A + ~A = -1andA + neg A =0Using these two formulas, we can getneg A = ~A + 1

int negate(int x)

  return ~x+1 ;
}

1.6 isAsciiDigit

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

thinking

Let’s take a look first0x39 and 0x30Bit level representation of

00111001and00110000First of all, we must meetx>>4==3Then I used some tips to satisfy the question that the last four bits are between 0 and 9

  1. x & 0xFThe last four digits of X are saved
  2. Judge the range of the last four digits by whether – A is a negative numberc=~0xA+1Implementation – A
  3. Judge that negative numbers are and0x8000And is a positive number
int isAsciiDigit(int x) {
    int a=!(x >> 4 ^0x3);
    int b=x&0xF;
    int c=~0xA+1;
    int e=0x80<<4;
    int d=!!((b+c)&(e));
  return  a&d ;
}

1.7 conditional

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */

thinking

x > 0 return y else return z

We need to find a way whenx != 0When does x become0xFFFFFFFF

int a=!!(x^0x0); //a=0 if x=0 else a =1
int b=~a+1;
int c=~(y&~b)+1;
int d=~(z&b)+1;

return y+z+c+dWe finally return this way. The meaning of the above code is actually very simple

Ifx!=0Then C will be equal to – y, and we can finally return Z, otherwise we will return y

1.8 isLessOrEqual

* isLessOrEqual - if x <= y  then return 1, else return 0 
*   Example: isLessOrEqual(4,5) = 1.
*   Legal ops: ! ~ & ^ | + << >>
*   Max ops: 24
*   Rating: 3
*/

thinking

Pay attention to direct usex-yIt may explodeintTherefore, such a simple judgment cannot be made

int a=x>>31&0x1;
int b=y>>31&0x1;
int c1=(a&~b); // Indicates that x is - y is+
int c2=(~a&b); // Represents x + y-

Let’s calculatey-xHere are some situations to consider

  1. y-x >= 0That is, the 32nd bit is 0flag=y+(~x+1)>>31=0At this time, ifc2 If C2 is 1, it means that C2 is overflowed. We should return0Ifc2=0Then we should return 1
  2. y-x <0beflag=1Return 0

Therefore, there are the following codes

int e=y+(~x+1); // x-y;
int flag=e>>31; // If the flag is different from C2, it indicates that the overflow is
return c1 |(!c2&!flag);

1.9 logicalNeg

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

thinking

if x!=0 return 0 else return 1Then the question becomes how to judgex!=0

Let’s take a look first~x+1>>31As long as X= 0, then all of them are – 1, and only when x = 0 is 0

So we usex |(~x+1>>31)If – 1, it means X= 0 means x = 0

int logicalNeg(int x) {
   return ((x | (~x +1)) >> 31) + 1;
}

1.10 howManyBits

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

thinking

This problem is to find the number of bits from right to left, the leftmost 1, and then add a sign bit. If it is a negative number, we take it negative, and then do the same operation. Then there will be the following situations

  1. X in [0,1]We need two
  2. X in [2,3]We need three
  3. X in [4,7]We need four
  4. Summary formula\(2^i\leq x \leqslant 2^{i+1}-1\)needi+2position

For the high 16 bits, we do this

x=(flag&~x)|(~flag&x); // If x is a non positive number, it remains unchanged, and if x is a negative number, it is equivalent to bitwise negation
int b16=!! (x>>16) <<4; // If the upper 16 bits are not 0, let B16 = 16
x>>=b16; // If the upper 16 bits are not 0, we move the 16 bits to the right to see the upper 16 bits

Then look at the high 8 bits, and the following processing is basically similar

//The following process is basically similar
  int b8=!!(x>>8)<<3;
  x >>= b8;
  int b4 = !!(x >> 4) << 2;
  x >>= b4;
  int b2 = !!(x >> 2) << 1;
  x >>= b2;
  int b1 = !!(x >> 1);
  x >>= b1;
  int b0 = x;
return b0+b1+b2+b4+b8+b16+1;

I suggest you manually simulate this process

1.11 floatScale2

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

thinking

1. First consider the first case

When argument is NaN, return argument

You need to find out firstexp

int exp = (uf&0x7f800000)>>23; // 23-30 these eight
int sign=uf>>31&0x1; // Sign bit
int frac=uf&0x7FFFFF;

Ifexp=255And the mantissa is non-zeroNaNJust return directly. Secondly, iffracAll 0 means infinity. You can return directly in both cases

  1. Ifexp=0Denormalization number

    So let’s go straight backuf*2Just putfrac>>1

  2. Ifexp!=0 && !=255Then it represents the normalized number

Then let’s change it firstexp+1

unsigned floatScale2(unsigned uf) {
    unsigned exp = (uf&0x7f800000)>>23;
    unsigned sign=uf>>31&0x1;
    unsigned frac=uf&0x7FFFFF;
    unsigned res;
    if(exp==0xFF)return uf;
    else if(exp==0){
        frac <<= 1;
        res = (sign << 31) | (exp << 23) | frac;
    }
    else{
     exp++;
     res = (sign << 31) | (exp << 23) | frac;
   }
   return res;
}

1.12 floatFloat2Int

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

thinking

The format of 6-bit IEEE floating point number is as follows

According to the above figure, we can divide it into three cases

First calculateE=exp-bias

  1. If decimalE< 0In this case, we directly return 0

  2. If it isexp=255Return directly to0x80000000uNote here that if the range is exceeded, 0x8000000u will also be returned directly

    Therefore, it can be used directlyE>=31To judge

  3. If it is a normalized number, we perform normal processing\(V=(-1)^s \times M \times 2^E\)

    1. First add the omitted 1 to the mantissa
    2. judgeE<23The mantissa needs to be rounded off23-Eposition
    3. Just return according to the sign bit
int floatFloat2Int(unsigned uf) {
    unsigned exp = (uf&0x7f800000)>>23;
    int sign=uf>>31&0x1;
    unsigned frac=uf&0x7FFFFF;
    int E=exp-127;
    if(E<0)return 0;
    else if(E >= 31){
        return 0x80000000u;
    }
    else{

        frac=frac|1<<23;
        If (e < 23) {// rounding required
            frac>>=(23-E);
        }else{
            frac <<= (E - 23);
        }

    }
    if (sign)
        return -frac;
    else
        return frac;
}

1.13 floatPower2

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

thinking

According to the above figure, we can draw several boundaries

  1. x>127Return + Nan
  2. X is too small, return 0
  3. x>=-126Normalized number
  4. Otherwise, it is denormalized
unsigned floatPower2(int x) {
    if(x>127){
        return 0xFF<<23;
    }
    else if(x=-126){
        int exp = x + 127;
        return (exp << 23);
    } else{
        int t = 148 + x;
        return (1 << t);
    }
}

The result shown in the figure above is correct