많은 프로그래밍 라이브러리에서 abs() 함수를 지원해주지만 가끔 그냥 만들어 쓸때도 있다.

한번 만들어보자.
type abs(type n){
	if(n<0)
		return -n;
	else
		return n;
}

라는 간단한 함수를 만들수 있다.
type 가 산술 연산이 가능한 타입이라면 모두 적용될것이다.

개인적으로 이때 나타나는 조건문이 조금 마음에 안들었다. 
그래서 일단 부동소수점에 대해서 약간 방식을 바꾸어 보았다.
부동소숫점은 IEEE754를 표준으로 하는데 Double이나 Float에 관계없이 첫번째 비트는 무조건 sign bit 즉 부호를 나타내는 비트가 된다.

그래서 절대값을 리턴하는 함수에서는 이것저것 따지지 말고 그냥  첫번째 비트만 0으로 바꾸어서 리턴 해주면 된다. 
하지만 C/CPP/JAVA 등의 프로그래밍 언어에서는 부동소숫점에 대한 비트연산을 허락하지 않는다.
여러가지 이유가 있겠지만 솔직히 부동소수점에다가 비트연산을 한다는것은 제대로된 결과를 내게 하는게 어렵기 때문이다.
 
하지만 방법은 있다.
공용체, 즉 union 이라는 타입을 사용하면 된다.
union에 대해 자세한 설명은 찾아보면 나오겠지만 하나의 메모리 공간을 여러가지의 타입으로 사용할수 있도록 한것이다.

만약 int 라는 4바이트의 메모리공간을 char[4] 라는 1*4 의 메모리 공간으로 쓸수 있는것이다.
따라서 float는 4바이트 부동소숫점이기 때문이 int 와 공용으로 사용하게 되면 float에서는 직접 비트연산을 할수 없지만
공용인 int 를 비트연산 해버리면 그것이 float에 적용이 된다.

따라서 이런 형태의 abs 함수를 생성할수가 있다. 

#include <stdio.h>
#include <stdlib.h>
typedef union float_abs{
	float f;
	int i;
}float_abs;
typedef union double_abs{
	double d;
	long l;
}double_abs;

float absf(float f){
	float_abs a={f};
	a.i&=0x7fffffff;
	return a.f;
}
double absd(double d){
	double_abs a={d};
	a.l&=0x7fffffffffffffff;
	return a.d;
}
int main(void) {
	printf("%lf\n",absd(-0.1));
	printf("%f\n",absf(-0.1));
	return EXIT_SUCCESS;
}

이런 히안한 형태의 abs 함수를 만들수 있게된다.
과연 if - else  를 이용한 abs와 비트연산을 이용한 abs와의 성능차이가 과연 있을까 하는 의문점이 생기긴 하지만
재미삼아 해봤다고 칩시다. 

int 형 정수의 경우는 2의 보수를 사용하기 때문에 실수형처럼 간단히 되지는 않겠지만 현제 if - else 없이 하는법을 생각중입니다. 

정수형의 경우에도 첫번째 비트가 사인비트인것은 변함이 없지만 첫번째 비트만 바꾼다고 절대값이 나오는것은 아니다.
음수일 경우에만 NOT 연산을 하고 +1 을 해야 하는데 이 음수일 경우에만 이라는것이 걸린다.
그렇다면 if문이 없이 어떻게 not을 하거나 하지 않거나 할수 있을까?
한가지 떠올린것은 XOR과 쉬프트 연산이었다.
일단 1000000...000 의 이진수로 and 연산을 하면 첫번째 비트의 값을 얻을수 있다.
이것을 오른쪽으로 31번 쉬프트를 시키면?
C에서는 signed 와 unsigned int 에 따라 연산이 달라지지만 기본적인 int 타입의 경우에는 sign bit를 유지해준다.

만약 결과값이 1000 이었다면 오른쪽으로 3번 쉬프트 한다면 1111이 된다. 
만약 0000 이라면 쉬프트 후에도 0000일것이다.
-참고로 1000.....00 으로 and를 안해도 상관없이 동작한다
이것을 이용해서 어떻게 not를 할까?

xor을 이용하면 된다!
xor은 같으면 0 다르면 1 을 만들어주는 특이한 연산이다.
이때 음수였다면 11111로 채워질것이므로 이것과 입력받은 숫자를 xor 한다면 1인 비트는 0으로 0인 비트는 1로 변한다.
양수였다면 0000으로 채워지므로 xor 해도 값이 변하지 않는다.

그렇다면 마지막 으로 해야할것은 음수일때는 not 를 하고 +1 을 해야하니까 '+1' 을 할 차례이다.
이것은 쉬프트를 하고 나온 값에다가 1을 and 하면된다.
음수였을때는 11111 이기때문에 1을 and하면 1이 될것이고 양수였을때는 0000이니까 1을 and하면 0이된다.
따라서 1을 and 한다음 나온 값을 원래 숫자에다가 더해주면 된다.

만들어진 코드는 다음과 같다.

int absi(int i){
	int t = i >> 31;
	return ( i ^ t ) + ( t & 1 );
}
long absl(long l){
	long t = l >> 63;
	return ( l ^ t ) + ( t & 1 ); 
}

int main()
{
	printf( "%d", absi(-100) );
	printf( "%ld", absl(-10000000000) );
	return 0;
}
 
-참고
절대 절대값을 만들수 없는 정수는 각정수형의 최소값.
자바에서 Integer.MIN_VALUE == -Integer.MIN_VALUE 인것을 알수있다.
-자바에서만 그런게 아니라 그냥 원래이럼
Posted by 동적할당
: