Home 부동 소수점 연산- 왜 0.1을 100번 더하면 10이 아닐까?
Post
Cancel

부동 소수점 연산- 왜 0.1을 100번 더하면 10이 아닐까?

코딩을 하면 변수 타입을 지정한다. 이 때 실수를 지정하는 타입은 float, double 이 두 가지가 존재한다.(C기준)

float는 4바이트(32비트) 표현, double은 8바이트(64비트)로 표현된다. double의 정확도가 더 높지만 더 큰 범위와 더 정확한 연산으로 리소스 소모가 더 심해 보통은 float를 우선적으로 사용한다.

소수점을 컴퓨터가 숫자를 표현하는 방식인 2진수로 저장하면 가끔씩 무한히 비트를 사용해야 하는 값들이 존재한다. 이는 치명적인 리소스 손실로 이를 막기 위해 두 가지 방법을 사용한다. 첫 번째는 고정 소수점. 두 번째는 이 글의 주제인 부동소수점이다. 부동소수점 표기를 더 일반적으로 사용하니 부동소수점에 대해 알아보자.

부동소수점 표기를 사용하면 왜 0.1을 100번 더했을 때 10이 아닌 값을 만들까?

image.png

부동소수점(Floating Point)이란

부동소수점은 컴퓨터 프로그래밍에서 실수를 근사적으로 표현하는 방법이다. 소수점을 고정하지 않고 둥둥 떠다닐 수 있게 한다 하여 부동(Floating) 이라는 단어를 사용한다.

정수부와 소수부로 나뉘며 지수를 사용해 실수를 표현한다. 부동소수점은 일반적으로 IEEE754 표준을 따르며 32비트와 64비트 표현을 정의한다.

image.png

예를 들어 0.3을 2진수로 표현하면

0.0100110011… (계속 반복되는 패턴) 으로 나타난다. 하지만 IEEE754 표현을 따르면

  1. 부호(Sign bit): 0.3은 양수이므로 부호비트는 0이다.

  2. 지수(Exponent): 먼저 0.3을 정규화(normalize)한다. 0.3을 2진수로 표현하면 0.0100110011…이고, 소수점 왼쪽에 첫 번째 1을 놓도록 정규화한다. 이를 위해 소수점을 왼쪽으로 이동시켜 1.00110011…×2(-2) 가 된다. 여기서 지수는 -2 이다.

  3. 가수(Fraction): 정규화된 값에서 가수 부분은 소수점 오른쪽에 위치한 비트들인 001100110011…. 이다.

따라서 0.3을 IEEE754 부동소수점 표준에 따라 표현하면

  • 부호: 0

  • 지수: 2의 -2승을 표현하는 8비트 2진수는 00000010 이다. 여기서 지수를 표현할 때 127을 더한 값을 사용하므로 2의 -2승의 지수에 127을 더하면 125가 된다. 이는 01111101이다.

  • 가수: 001100110011…의 부분을 23비트로 표현. 처음 23자리값은 00110011001100110011011

결론

0.3을 IEEE754 표준에 따라 표현하면

0 01111101 00110011001100110011011 이 된다.

왜 부동소수점 연산에는 오류가 발생할까.

우리가 코딩을 할 때 변수를 실수로 지정할 때 어떻게 하는가.

1
2
3
4
float Hello = 0.3; 

// 변수타입 SP 변수 SP = SP 실수;

형태로 지정한다. 우리가 보이기엔 ‘아! Hello라는 이름의 변수는 값이 0.3이구나!’ 라고 생각하지만 언어를 컴파일하는 컴퓨터에선 Hello라는 이름의 변수 안에 상단에 작성한 0.3의 IEEE754 부동소수점 표현의 값이 저장된다. 하지만 0.3의 2진법을 정확히 표기하면0.0100110011… (계속 반복되는 패턴)이 나오는데 부동소수점 표기는 이와 다르다.

결국 효율적인 리소스 관리를 위해 메모리에서 표기할 수 있는 최적의 근사값으로 저장하니 결과가 달라지는 거고 이 달라진 결과가 누적되면 누적 결과는 원하는 값에서 크게 멀어지게 된다.

해결

그럼 실수 계산은 평생 못하는건가? 그건 아니다. 다양한 해결책이 존재한다.

1. 정수 변환 연산

간단하다. 실수를 정수로 변환해 반복되는 계산을 진행한 뒤 다시 정수를 실수로 변환한다.

image.png

많이 사용하는 방법. 반올림 오차를 피할 수 있어서 정확한 결과를 얻을 수 있지만 상황에 따라 리소스 소모가 더 클 수도 있고 불가할 때도 존재한다.

2. 부동소수점 연산 라이브러리 사용

decimal.Decimal, math.fsum(), round() 등의 라이브러리를 사용한다.

파이썬-Decimal 라이브러리링크

image.png

이미지의 위 수식과 아래 수식은 수학적으로는 같으나 결과가 다르다.

단, 해당 라이브러리는 파이썬 버전마다 결과가 달라질 수도 있으니 주의하자.

참고

모도리님 작성글-부동소수점이란

Wiki-Floating Point

This post is licensed under CC BY 4.0 by the author.