IEEE 754 부동 소수점 반올림과 근사

컴퓨터는 단지 숫자다 :: 2. 부동 소수점 반올림과 Rounding

컴퓨터는 단지 숫자다 :: 근사와 Rounding

서론

프로그래밍 언어는 어떤 언어든지 완전하지 못하다. 이번 파트에서는 프로그램 언어가 가지는 태생적인 결함 중에서 근사에 대한 얘기를 해보려고 한다.
플라톤은 자신의 철학 사상을 얘기하면서 '이데아’를 주장하였다. 사람이 그린 어떤 삼각형도 모든 내각의 합이 결코 정확한 180도가 될 수 없지만 세상 밖에서는 모든 내각의 합이 정확히 180도인 삼각형의 원형이 존재한다는 사상이었다. 컴퓨터마저도 수를 표현함에 있어 '이데아’를 실현하지는 못한다. 예를 들어 0.1이라는 수를 컴퓨터는 정확히 표현할 수 없어
0.10000000000000000555111512... 이라는 값으로 저장한다. 정밀도에 따라 32비트 혹은 그 이상이 될 수 있겠지만 중요한 것은 컴퓨터도 때로는 정확한 값을 저장할 수 없다는 말이다. 이러한 오차는 별 것 아니라고 생각할 수 있겠지만 제대로 컨트롤 하지 못하는 수의 오차는 생각보다 심각한 문제를 일으키기도 한다.

1991년 2월 25일 1차 걸프 전쟁 기간중 사우디아라비아 Dharan에 위치한 미국 패트리엇 미사일 부대는 날아오는 이라크의 스커드 미사일을 격추하는 데 실패해 28명의 대원이 사망했다. 미국 조사위는 이 실패에 관해 상세한 조사를수행한 결과 수치 계산의 부정확성이 주요 원인이라는 결론을 내렸다. 패트리엇 시스템은 내부적으로 0.1초마다 증가하는 카운터가 있었고 0.1초를 내부적으로 24비트에 저장해 근사값으로 더하고 있었다. 이 때 0.1초의 오차는 220×1102^{-20}\times{1\over10} 이었고 약 100시간동안 패트리어트 시스템이 동작하고 있었고 스커드 미사일이 2000m/s로 날아오고 있었다. 그 결과 격추 시스템의 오차는 약 680m로 28명의 대원이 사망하였다

이번 주제 :: 올바른 Rounding

IEEE 754에 따르면 수학시간에 배우는 반올림 방법은 IEEE에서 표준으로 지정한 Rounding 방법이 아니다. 0.5는 1로 반올림한다고 배웠지만 대부분의 컴퓨터는 그렇게 동작하지 않는다. 그나마 표준 Rounding 방법 중 To nearest, Ties-to-even이 우리가 아는 '반올림’이라는 개념과 유사하다. 아래에서 다루겠지만 Ties-to-even은 정수와 정수 정중앙에 위치한 .5값은 가까운 짝수쪽으로 올림하거나 내림한다. 그렇다면 올바른 To nearest, Ties-to-even 방법은 다음과 같다.

  • 2.4 : 2
  • 2.5 : 2
  • 2.6 : 3

5가지 Rounding 방법

Rounding을 한국어로 번역할만한 단어가 좀처럼 생각나지 않아 그대로 가져와 사용했다. 올림과 내림, 반올림, 반내림 모두 Rounding을 표기하기에는 정확하지 않은 표현이기 때문이다.
Rounding이란 영어 뜻으로는 정돈하다. 다듬다정도로 번역할 수 있고 컴퓨터 용어로는 원래 수를 어느정도 유지하면서 자리수를 원하는 만큼까지 줄이는 방법이라고 해석할 수 있겠다. 자리수를 줄이기 위해서는 원하는 자리수 아래로의 수들을 어떻게 처리할지 정책이 필요하게 되는데 IEEE에서 서술한 공식 Rounding 방법에는 5가지가 있다.

  • To nearest, Ties-to-even
  • To nearest, Ties-away-from-zero
  • Round-to-down
  • Round-to-up
  • Round-toward-zero

To nearest, Ties-to-even : 원하는 자리수 아래의 값들이 5를 기준으로 크거나 작으면 각각 Round-to-up, down을 적용한다. 만약 정확히 5라면 다음 자리수를 기준으로 위의 과정을 반복한다. 만약 모든 자리 값이 5라면 최종 값이 근접한 짝수가 되도록 1을 더하거나 뺀다.

To nearest, Ties-away-from-zero : 원하는 자리수 아래의 값들이 5를 기준으로 크거나 작으면 각각 Round-to-up, down을 적용한다.만약 정확히 5라면 다음 자리수를 기준으로 위의 과정을 반복한다. 만약 모든 자리 값이 5라면 최종 값이 0에서 먼 방향으로 1을 더하거나 뺀다.

Round-to-down : 원하는 자리수 값들을 모두 버린다.

Round-to-up : 원하는 자리수 아래의 값들을 버리고 최종 값에 1만큼의 값을 올려준다.

10진수 Rounding :: To nearest, Ties-to-even

To nearest 경우 더 가까운 쪽에 근사
1.23456789
소수 첫 번째 위치에 Rounding : 1.2
소수 두 번째 위치에 Rounding : 1.23
소수 세 번째 위치에 Rounding : 1.235

정확히 중간 값일 경우 가까운 짝수에 근사. 소수에서 짝수는 근사할 위치의 수가 짝수일 경우에만 해당된다.
1.25
소수 첫 번째 위치에 Rounding : 1.2
1.35
소수 첫 번째 위치에 Rounding : 1.4

2진수 Rounding :: To nearest, Ties-to-even

2진수 Rounding은 익숙하진 않지만 동일한 원리로 적용할 수 있다. 10진수로 바꾸고 나서 Rounding하는 것이 아니라 2진수에서 Rounding을 한다.

To nearest

  • 0.11120.111_{2}
    소수 첫 번째 위치에 Rounding : 1.021.0_{2}

  • 0.10120.101_{2}
    소수 첫 번째 위치에 Rounding : 0.120.1_{2}

Ties-to-even

  • 0.0100020.01000_{2}
    소수 첫 번째 위치에 Rounding : 0.020.0_{2}

  • 0.1100020.11000_{2}
    소수 첫 번째 위치에 Rounding : 1.021.0_{2}

To nearest의 원칙은 어느 쪽에 가까운지를 판단해서 가까운 쪽은 선택한다 10진수에서는 5를 기준으로 어느쪽에 더 가까운지를 알 수 있었다면 2진수에서는 1이 그 기준의 역할을 한다. 2진수에서 1은 바로 왼쪽 비트의 1이 나타내는 수의 절반을 나타낼 수 있다. Rounding하려는 위치의 그 다음 비트들이 우리가 관심있는 비트이다. 0.11ˉ120.1\bar{1}1_{2}의 소수 두 번째 자리를 보면 1 그리고 0.111ˉ20.11\bar{1}_{2} 세 번째 자리도 1이다. 소수 첫 번째 자리가 표현할 수 있는 크기가 1이라면 절반에 절반의 절반이 더해졌으니 이미 중앙을 넘었다 그래서 소수 첫 번째 위치에 1이 올라가 1.021.0_{2}이 된 것이다.
0.10120.101_{2}의 경우에는 0.10ˉ120.1\bar{0}1_{2} 소수 두번 째 자리가 0이다. 이 경우에는 그 뒤에 어떤 값들이 오더라도 절반이 되거나 절반을 넘을 수 없다.

Ties-to-even의 경우를 살펴보려고 한다. 0.01ˉ00020.0\bar{1}000_{2}. 소수 두 번째 자리가 1이기 때문에 절반이 되었고, 그 뒤에는 모두 0이기 때문에 어떤 값도 더해지지 않아 정확히 절반에 위치한다. 이럴 경우 Ties-to-even 룰을 적용해줘야 합니다. 2진수에서 짝수란 LSB가 0인 경우를 가르킨다. 즉 Rounding 할 위치보다 작은 비트를 모두 버리거나 Rounding할 위치에 1을 올려보내 Rounding할 위치의 값이 0이 되도록 하면 Ties-to-even를 올바르게 구현한 것이다.

Imgur

2진수 Rounding 요약

  1. 2진수 Rounding할 위치 다음 비트가 0이라면 버림
  2. 2진수 Rounding할 위치 다음 비트가 1이고 뒤를 잇는 비트 중 하나라도 1이라면 올림
  3. 2진수 Rounding할 위치 다음 비트가 1이고 그 뒤를 있는 모든 비트가 0이라면 짝수가 되도록 버리거나 올림.

다음 주제 :: Rounding은 어떻게 구현될까.

  1. 우리는 0과 1로 정수와 소수를 표현하는 방법을 배웠고 부동 소수는 오차를 가지기 때문에 0.499999999와 같은 값을 반올림 하게되면 0이 아닌 1이 나올 수도 있다는 사실도 알았다. Round, 혹은 ceil, floor함수는 이를 어떻게 해결할까?
  2. 컴퓨터는 Rounding을 할 때 10진수로 바꾸어 사람처럼 Rounding을 할까? 아니면 2진수에서 Rounding을 할까?
  3. 프로그래머는 컴퓨터가 가지는 부동 소수점 오차 문제를 어떻게 해결할 수 있을까?

댓글

이 블로그의 인기 게시물

[Linux, Unix] export, echo 명령어 알아보기