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으로 만들어주면 된다.
uf의 sign 비트를 0으로 만들어주기 위해서는 uf와 0x7FFFFFFF를 &연산해주면 된다.
부동 소수점의 필드는 s, exp, frac으로 나누어져 있으며, NaN이 나올 수 있다. 따라서 NaN가 아닌 경우에만 uf의 sign 비트를 0으로 만들어준 것을 리턴한다.
uf와 0x7F800000를 &연산을 하면 uf의 exp 부분만 값이 남게 되고, uf와 0x007FFFFF를 &연산 하면 uf의 frac 값만 남게 된다. 이때 NaN의 조건이 exp가 11111111이고 frac != 00000000 일 때이다. 만약 uf가 NaN가 나올 경우 인자로 받은 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을 리턴하면 된다. 이때 x와 y의 부호는 가장 왼쪽 1비트의 값이기 때문에 x와 y 둘 다 각각 31만큼 right shift 해서 부호 비트를 제일 끝에 옮겨주고 0x01과 &연산을 해서 부호를 변수 x_와 y_에 저장한다. x+y를 저장하는 변수인 sum은 x+y를 하고 부호를 확인하기 위해 위에서 했던 과정을 반복해준다.
x, y, sum의 부호에 관한 카르노 맵을 그려서 return 값을 정한다.(아래 그림 참조)
x와 y가 0이고 x+y가 1일 때 오버플로가 발생한 것이기 때문에 이 경우에 0이 되고, x와 y가 1이고 x+y가 0일 때 오버플로가 발생한 것이기 때문에 이 경우에도 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);
}
① x의 n 바이트 자리를 0으로 만든다.
x의 n 바이트 자리를 0으로 만들기 위해서 &연산자를 활용한다. x의 n 바이트 자리의 위치만 0이고 나머지는 1인 비트를 만든다. 그 비트와 x를 & 하면 x의 n 바이트 자리는 0이 되고 나머지 수는 그대로 유지할 수 있다.
이때 x의 n 바이트 자리의 위치만 0이고 나머지는 1인 비트는 0xFF를 이용해 만들 수 있다. 0xFF를 n 바이트 만큼 leftshift 시키면 FF000....이 된다. x의 바이트 자리의 위치에 F가 오고 나머지는 0이 온 형태이다. 따라서 leftshift 한 0xFF에 ~을 붙이면 원하는 00FF..로 만들 수 있다.
② c를 n 바이트 만큼 leftshift해서 x의 n바이트와 위치를 맞춘다.
③ 1번의 x와 2번의 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;
}
x가 0이 아닌 경우, x나 –x 둘 중에서 부호가 1이 되는 것이 있을 것이다. x가 0이면 –0과 0이 같기 때문이다. 따라서 x와 –x를 | 연산을 이용해 0이 아닌 경우 a의 부호가 1이 되도록 한다. -x를 만드는 방법은 논리회로에서 배웠듯이 2의 보수를 취해주면 된다. 2의 보수를 취하는 방법 중 ~을 붙이고 1을 더하는 것이 있었다. 따라서 ~x+1로 표현할 수 있다.
a를 31만큼 right shift를 해서 부호 비트를 제일 오른쪽에 위치하도록 한다. right shift를 하면 왼쪽이 부호 비트와 같은 것으로 채워지게 된다. 즉 x가 0이 아닐 때에는 0xFFFFFFFF이 a에 들어있을 것이고, 0일 경우 0이 a에 들어있을 것이다. 이때 a과 1을 & 하게 되면 맨 끝의 값만 남게 되기 때문에 x가 0이 아니면 1을 리턴할 수 있고, x가 0이면 0 그대로 리턴할 수 있게 된다.
'System Programming > Data Lab' 카테고리의 다른 글
Datalab에 필요한 기본지식 (0) | 2022.01.03 |
---|