1. int bitOr(int x, int y)

- 비트 OR을 연산하여 리턴

- 사용 가능 연산자 : ~ &

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

함수적 완전성으로 x | y를 &로 표현할 수 있다. x | y에 ~를 붙이면 ~(x|y) => ~x & ~y임을 알 수 있다. 

∴ x | y = ~(~x & ~y) 

 

2. int tmin(void)

- 2의 보수 중 가장 작은 값을 리턴

- 사용 가능 연산자 : ! ~ & ^ | + << >>

int tmin(void) {
	int a = 1;
	return a<<31;
}

2의 보수 중 가장 작은 값은 1000 0000 0000 0000 0000 0000 0000 0000 이다.

(뒤에 하나라도 1이 있으면 그 자리에 해당하는 수가 더해지기 때문에 뒤가 모두 0이고 맨 앞만 1인 수가 제일 작은 값이된다.)

따라서 1을 31번 left shift 하면 2의 보수 중 가장 작은 값을 구할 수 있다.

 

3. int evenBits(int x)

- 짝수 자리의 비트 값을 1로 세팅하여 리턴

- 사용 가능 연산자 : ! ~ & ^ | + << >>

int evenBits(void) {
    int a = 0x55;
    int b = 0x55;
    int c = 0x55;
    int d = 0x55;
    return (a<<24)|(b<<16)|(c<<8)|d;
}

짝수 자리의 비트 값을 1로 세팅한다는 것은 0101 0101 0101 0101 0101 0101 0101 0101, 즉 0x55555555를 리턴하라는 것이다. 변수 4개에 0x55를 나누어 세팅해주고 left shift를 이용해 0x55555555를 만들어주었다.

 

4. int getByte(int x, int n)

- x의 수에서 n 바이트 자리를 추출하여 리턴

- 사용 가능 연산자 : ! ~ & ^ | + << >>

int getByte(int x, int n) {
    int a = x>>(n<<3);
    return a&0xFF;
}

x에서 n 바이트 자리를 추출하려면 x에서 n 바이트 만큼 right shift 시켜서 리턴하려는 n 번째 바이트를 가장 오른쪽으로 올 수 있게 만들어야 한다. n을 3만큼 left shift 시켜서 n에 8을 곱해준다. (byte 단위이기 때문에 8을 곱했다.) nx8한 것을 x에서 right shift 를 통해 원하는 n 번째 바이트가 가장 끝에 오도록 만들어서 변수 a에 저장한다.

그 후 0000 0000 0000 0000 0000 0000 1111 1111인 0xFF와 & 연산을 시켜서 마지막 1 byte만 남을 수 있도록 하였다. 이때, 0xFF를 함으로써 right shift에서 산술 자리 이동으로 인해 생기는 1을 고려하지 않아도 된다.

 

5. unsigned float_abs(unsigned uf)

- 절댓값을 취한 부동 소수점의 비트 표현 값 리턴

- 사용 가능 연산자 : integer/unsigned 연산자 모두 사용 가능. || && if while 등

unsigned float_abs(unsigned uf) {
    unsigned abs = 0x7FFFFFFF & uf; // uf의 sign bit을 0으로
    unsigned exp = 0x7F800000 & uf;	// uf의 exp 부분
    unsigned frac = 0x007FFFFF & uf; // uf의 frac 부분
    
    // exp가 11111111 이고 frac 이 0이 아니면 NaN
    if((exp==0x7F800000) && frac) {
    	return uf;
    } 
    else {
    	return abs;
    }
}

unsigned로 들어온 uf를 부동 소수점으로 생각한 뒤 sign 비트를 0으로 만들어주면 된다.

ufsign 비트를 0으로 만들어주기 위해서는 uf와 0x7FFFFFFF를 &연산해주면 된다.

부동 소수점의 필드는 s, exp, frac으로 나누어져 있으며, NaN이 나올 수 있다. 따라서 NaN가 아닌 경우에만 ufsign 비트를 0으로 만들어준 것을 리턴한다.

uf0x7F800000&연산을 하면 ufexp 부분만 값이 남게 되고, uf와 0x007FFFFF를 &연산 하면 uffrac 값만 남게 된다. 이때 NaN의 조건이 exp11111111이고 frac != 00000000 일 때이다. 만약 ufNaN가 나올 경우 인자로 받은 uf를 그대로 리턴해준다.

 

6. int addOK(int x, int y)

- 오버플로우 없이 x+y가 가능하면 1을 리턴, 불가능하면 0을 리턴

- 사용 가능 연산자 : ! ~ & ^ | + << >>

int addOK(int x, int y) {
    int x_ = (x>>31) & 0x01;
    int y_ = (y>>31) & 0x01;
    int sum = (x+y) >> 31;
    sum = sum & 0x01;
    return (!x_&!sum) | (y_&sum) | (x_&!y_);
}

오버플로가 생길 수 있는 경우의 수는 2가지이다. x>0, y>0 일 때 x+y <0 인 경우와x<0, y<0 일 때 x+y >0 인 경우이다.

문제를 쉽게 바꾸면 오버플로가 발생하면 0을 리턴하고 발생하지 않으면 1을 리턴하면 된다. 이때 xy의 부호는 가장 왼쪽 1비트의 값이기 때문에 xy 둘 다 각각 31만큼 right shift 해서 부호 비트를 제일 끝에 옮겨주고 0x01&연산을 해서 부호를 변수 x_y_에 저장한다. x+y를 저장하는 변수인 sumx+y를 하고 부호를 확인하기 위해 위에서 했던 과정을 반복해준다.

x, y, sum의 부호에 관한 카르노 맵을 그려서 return 값을 정한다.(아래 그림 참조)

xy0이고 x+y1일 때 오버플로가 발생한 것이기 때문에 이 경우에 0이 되고, xy1이고 x+y0일 때 오버플로가 발생한 것이기 때문에 이 경우에도 0이 된다. 두 경우를 제외한 나머지 경우는 1이 된다.

 

7. int replaceByte(int x, int n, int c)

- x의 수에서 n 바이트 자리를 c로 교환한 값을 리턴

- 사용 가능 연산자 : ! ~ & ^ | + << >>

int replaceByte(int x, int n, int c) {
    int n_index = n << 3;
    int number = ~(0xFF << n_index);
    int n_zero = x & number;
    
    return n_zero | (c << n_index);
}

① xn 바이트 자리를 0으로 만든다.

xn 바이트 자리를 0으로 만들기 위해서 &연산자를 활용한다. xn 바이트 자리의 위치만 0이고 나머지는 1인 비트를 만든다. 그 비트와 x& 하면 xn 바이트 자리는 0이 되고 나머지 수는 그대로 유지할 수 있다.

이때 xn 바이트 자리의 위치만 0이고 나머지는 1인 비트는 0xFF를 이용해 만들 수 있다. 0xFF를 n 바이트 만큼 leftshift 시키면 FF000....이 된다. x의 바이트 자리의 위치에 F가 오고 나머지는 0이 온 형태이다. 따라서 leftshift 한 0xFF에 ~을 붙이면 원하는 00FF..로 만들 수 있다.

② cn 바이트 만큼 leftshift해서 xn바이트와 위치를 맞춘다.

③ 1번의 x2번의 c| 연산을 통해 합친다.

 

8. int inNonZero(int x)

- x가 0이 아닐 경우 1을 리턴, 0인 경우 0을 리턴

- 사용 가능 연산자 : ! ~ & ^ | + << >>

int isNonZero(int x) {
    int a = x | (~x+1);
    a = a >> 31;
    return a & 1;
}

 x0이 아닌 경우, xx 둘 중에서 부호가 1이 되는 것이 있을 것이다. x0이면 00이 같기 때문이다. 따라서 xx| 연산을 이용해 0이 아닌 경우 a의 부호가 1이 되도록 한다. -x를 만드는 방법은 논리회로에서 배웠듯이 2의 보수를 취해주면 된다. 2의 보수를 취하는 방법 중 ~을 붙이고 1을 더하는 것이 있었다. 따라서 ~x+1로 표현할 수 있다.

a31만큼 right shift를 해서 부호 비트를 제일 오른쪽에 위치하도록 한다. right shift를 하면 왼쪽이 부호 비트와 같은 것으로 채워지게 된다. x0이 아닐 때에는 0xFFFFFFFF이 a에 들어있을 것이고, 0일 경우 0a에 들어있을 것이다. 이때 a1& 하게 되면 맨 끝의 값만 남게 되기 때문에 x0이 아니면 1을 리턴할 수 있고, x0이면 0 그대로 리턴할 수 있게 된다.

 

728x90

'System Programming > Data Lab' 카테고리의 다른 글

Datalab에 필요한 기본지식  (0) 2022.01.03

(※충남대학교 컴퓨터융합학부 2020 시스템프로그래밍 권진세교수님 수업을 듣고 정리한 내용입니다)

 

* Boolean Algebra(불 대수)

- 논리를 표현하기 위한 대수학

- 컴퓨터 내부의 비트 정보 표시에 사용하는 변수들은 0 또는 1을 가진다

- 2진수의 표시 및 연산에 유용

 

① AND(&)

& 0 1
0 0 0
1 0 1

AND는 두 변수 중 하나라도 0이 있으면 결과가 0이다. 즉 1이 나오려면 둘 다 1인 경우밖에 없다.

 

② OR(|)

| 0 1
0 0 1
1 1 1

OR는 두 변수 중 하나라도 1이 있으면 결과가 1이다.

 

③ NOT(~)

~ 0
0 1
1 0

~0은 1이고 ~1은 0이다.

 

④ Exclusive OR(XOR, ^)

^ 0 1
0 0 1
1 1 0

두 변수가 같으면 0 다르면 1이다. 

 

* 비트 연산자

- shift 연산자 (<<, >>)

: 어셈블리 언어나 기계어의 프로그램 작성에서 러지스터 또는 기억 장소 내에 비트 값들을 왼쪽이나 오른쪽으로 이동시키는 것

 

- Left shift (x << y)

: 좌측을 y 개수만큼 없애고 우측에 y 개수만큼 0을 채워줌

 

- Right shift(x >> y)

: 우측을 y 개수만큼 없애고 좌측에 y 개수만큼 0을 채워줌

이때 논리적 자리 이동의 경우 좌측 맨 앞자리를 무조건 0으로, 산술 자리 이동의 경우 현 x의 부호를 유지

만약 x의 부호가 양수라면 0, 음수라면 1을 좌측 맨 앞자리에 채우게 됨

 

- 예시

(Argument) x 1010 0010
x << 3 0001 0000
(Logical) x >> 2 0010 1000
(Arithmetic) x >> 2 1110 1000

 

728x90

'System Programming > Data Lab' 카테고리의 다른 글

Datalab bits.c 함수 설명 및 풀이  (0) 2022.01.05

+ Recent posts