본문 바로가기
시스템프로그램

[시스템프로그램]02-3-3 실수와 사칙연산(3)

by 케찹이 2020. 4. 30.

저번 수업까지 실수에 대해서 배웠다면 이제부터 실수의 사칙연산에 대해 배워본다. 

 

저번 시간에 floating point(실수)는 모든 수를 표현할 수 없다고 배웠다. 그래서 그러한 단점들을 보안하기 위해 실수는 rounding이란 것이 필요하다. Rounding이란 우리가 예전 수학시간에서 배웠던 반올림과 비슷한 개념이다. 정수를 표현했을 때는 모든 수들을 차례로 표현이 가능했지만 실수에서는 그런 것이 불가능하여 Rounding을 사용한다. 

 

실수의 Rounding은 총네가지가 있다. 

Round-to-even

Round-toward-zero

Round-down

Round-up

 

이중에서 제일 많이 쓰는 rounding이 바로 round to even이다. 항상 모든 컴파일러들의 실수 round의 default를 round to even이라고 생각하면 된다. 다른 rounding방식도 때에 따라서 사용한다. 

위 표를 통해서 round to even방식을 얘기하자면 1.4는 1과 가까움으로 1로 반올림한다. 1.6은 2와 가까우므로 2로 반올림한다. 우리가 수학문제를 풀었을 때 0.5같은 경우는 그냥 큰 값으로 반올림 하였다. 하지만 round to even은 이 rounding의 이름 답게 even(짝수)쪽으로 반올림한다. 그러므로 1.5는 2로 반올림한다. 그리고 2.5도 짝수쪽으로 반올림하여 2가된다. -1.5도 짝수쪽으로 -2가 된다. 

round toward zero는 이전에도 말한적 있듯이 0과 가까운쪽으로 반올림한다. 

round down은 무조건 작은 값을 반올림, round up은 무조건 큰 값으로 반올림한다. 

 

Binary 표현에서 round to even을 하면 LSB가 0이면 짝수고 LSB가 1이면 홀수라고 생각하면 된다. 그럼 예를 들어서 주어진 실수의 소수점 두번째 자리로 round to even을 하게 된다면 

10.11100 은 소수점 두번째 자리라고 했으니 그 뒤에 있는 100을 살펴보아야 한다. 100은 방금 0.5와 같이 표현가능한 범위의 딱 중간이다. 그러므로 10.11은 짝수로 가야하니 11.00이 된다. 

10.10100은 반대의 예가 된다. 이 값은 LSB가 이미 짝수이니 10.10이 되겠다. 

 

 

Rounding을 배웠으니 실수의 사칙연산들을 배운다. 일단 실수의 곱셈부터 배운다. 

 

(-1)^s1 * M1 *  2^E1       *        (-1)^s2 * M2 * 2^E2

 

위 두 실수를 곱하는 상황이다. 두 실수를 곱하면 실수 값이 될테니 아래와 같을 것이다. 

 

(-1)^s * M *  2^E

 

여기서 sign값을 먼저 살펴보자면 부호가 같다면 무조건 양수 다르면 양수인 것을 보아 s = s1 ^(XOR) s2로 표현가능하다. 

M값은 그냥 곱해주면 된다. M= M1 * M2

E값도 그냥 지수가 곱하는 방식을 따라주면 된다. E = E1 + E2

 

저번 정수에서도 겪었듯이 결과값이 주어진 비트보다 컸을 때에는 fixing이 필요하다. 여기서 M값이 2보다 커지게 된다면 M을 오른쪽으로 swift하고 E을 올려주면 된다. 예를 들어서 M을 1만큼 swift했으면 E를 1만큼 증가시켜줘야한다.

그러고나서 E가 범위를 벗어나게 된다면 overflow가 된다. 마지막으로 swift되었던 M을 주어진 fraction에 맞게 truncate해주어야한다. 

 

 

실수의 덧셈은 곱셈보다 조금 더 복잡하다. 

 

(-1)^s1 * M1 *  2^E1       +        (-1)^s2 * M2 * 2^E2

 

두 실수가 덧셈을 진행하고 있고 첫번째 실수의 지수가 두번째 지수보다 크다고 가정하자 (E1 > E2)

그럼 두 실수의 합은 실수이니 밑과 같이 나오게 된다.

 

(-1)^s * M *  2^E

 

sign값을 확인 하는 방법은 두 실수의 절대값을 확인해보아야 한다. 만약에 두 실수의 부호가 같다면 바로 sign값을 판단할 수 있지만 두 수의 부호가 다르다면 두 수의 절대값을 확인해 더 크거나 작은 수에 맞춰 따라가야한다. 

M은 그대로 더하면 안돼고 일단 지수값을 맞춘 후(sign align) 덧셈을 진행해야 한다. 일반적으로 더 큰 지수 E1에 따라 E2를 맞춘다.

E는 E2가 E1에 맞추니 E = E1이 된다. 

 

덧셈에서도 마무리로 몇가지 사항들을 살펴보아 fixing을 진행해야 한다. 

-M이 2보다 클 경우 M을 오른쪽으로 shift 그에 맞춰 E값을 증가 시켜주어야 한다. 

-M이 1보다 작을 경우 M을 왼쪽으로 shift, 그리고 그에 맞춰 E값을 감소 시켜 주어야한다. 

-증감시킨 E값이 범위 밖으로 나가면 overflow가 된다.

-마지막으로 shift된 M을 fraction위치에 맞게 truncate시켜준다. 

 

 

 

Floating Point의 몇가지 연산 특징들을 살펴본다. 컴퓨터에서의 실수 연산들은 수학에서의 몇몇 법칙들이 성립되지 않는다. 

첫번째로 실수의 덧셈 결합법칙이 성립되지 않는다. 예를 들어서 

 

(3.15 + 1e10) - 1e10 = 0.0

3.15 + (1e10 - 1e10) = 3.15

 

1e10은 1곱하기 10의 10승을 뜻한다. 우리가 32비트를 사용할때 fraction비트가 23비트인데 이는 소수점 아래 7자리 정도 표현이 가능한 수이다. 근데 1e10은 10자리까지 표현가능하므로 3.15 + 1e10은 10000......3.15가 될텐데 밑에 3자리는 생략되어 그냥 1e10이 되어 결국 결과값은 0.0이 된다. 반면에 1e10 - 1e10을 먼저 계산하면 두 값의 차는 0이 되므로 최종적으로 3.15가 된다. 

 

두번째로 곱셈의 결합법칙도 성립하지 않습니다. 예를 들어서 

 

(1e20*1e20)*1e-20 =  

1e20*(1e20*1e-20) = 1e20

 

이것도 덧셈 결합법칙에서 보았던 것과 비슷하다. 1e20 * 1e20을 하게 되면 이미 가 되고 와 어떤값을 곱해도 이기때문에 결과값은 가된다.

반면에 밑 1e20 * 1e-20은 1이 되고 1 곱하기 1e20은 1e20이 된다. 그러므로 성립이 안된다.

 

마지막으로 곱셈의 분배법칙도 성립되지 않는다. 예를 들어서 

 

1e20*(1e20-1e20) = 0.0

1e20*1e20 - 1e20*1e20 = NaN

 

이 또한 실수의 성질 때문에 성립이 안되는 것을 알 수 있다. 

 

다만 a>=b일때 x + a >= x + b는 x,a,b가 NaN이 아니라고 할때 성립한다. 

 

 

그럼 C에서 실수가 어떻게 사용되냐면 주로 우리가 사용하는 실수변수로 float와 double이 있다. float는 32비트, double은 64비트이다. 앞에서 언급을 했지만 C언어는 IEEE FP가 일반적으로 적용되지 않았다. 그래서 rounding을 할때 그냥 overflow라고 오류를 내기도 하고 그냥 쓰레기 값을 던져주기도 한다. 

그리고 ISO C99에서는 long double이란 변수를 지원하는데 지 변수는 80비트를 사용할 수 있고 실수에서 sign비트는 1비트, 15비트의 exp비트를 차지하고 63비트만큼 frac비트를 차지한다. 나머지 1비트는 그냥 버린다. 

 

마지막으로 형변환에 대해서 살펴보고자 한다. 

-int값을 float로 변환하면 int값은 정수이므로 32비트를 통째로 저장하지만 float값은 23비트만 fraction을 저장할 수 있기에 rounding이 일어난다. 

-int또는 float가 double로 변환하면 double은 52비트의 frac비트를 보유하고 있으므로 정상적으로 conversion이 가능하다. 

-double에서 float는 당연히 float의 frac bit가 부족하므로 rounding이 발생하고 

-float또는 double이 int로 저장되는 과정에서도 rounding이 발생한다. 

 

실수는 사실 컴퓨터 전공 분야에서는 많이 사용되지는 않는다. 컴퓨터 운영체제만 보아도 실수는 하나도 없고 정수만 존재하고 오히려 수학분야에서 굉장히 많이 사용된다고 한다.

Ariane 5 tragedy라고 1996에 발생한 우주선 사고가 있는데 무려 37초만에 공중에서 폭파되었다. 사고의 원인이 바로 실수였다고 한다. 이전에 Ariane 4까지만 해도 16비트를 사용했는데 이를 확장하는 과정에서 계산이 잘못되었다고 한다. 

댓글