목차
부동소수점
부동소수점은 실수를 표현하기 위한 방법 중 하나로, 이는 소수점의 위치가 고정되어 있지 않고 "부동"할 수 있기 때문에 이런 이름이 붙었습니다.
부동소수점 수의 구조
1.
부호 : 비트는 수가 양수인지 음수인지를 나타냅니다. 0은 양수를 의미하고, 1은 음수를 의미합니다.
2.
지수 : 이 부분은 소수점의 위치를 결정하며, 수의 크기 또한 결정합니다. 지수는 편향된 형태로 저장되기 때문에 실제 사용 시에는 일정 값(예: 127 또는 1023)을 빼서 실제 지수를 계산합니다.
127은 01111111과 같이 2진수로 표현할 수 있습니다. 지수부의 맨 처음에 0을 쓰고 나머지를 1로 채운 값을 빼는 것이 바이어스 편향입니다. 이를 통해 -127에서 128까지의 지수부를 표현할 수 있습니다.
3.
가수 : 이 부분은 수의 실제 숫자 값을 저장하며, 표준에서는 '1.xxxx...' 형태로 정규화된 가수를 사용하는데, 이 때 가장 앞의 '1'은 대개 저장되지 않고 암시적으로 처리됩니다.
IEEE 754 표준
C#의 float와 double 타입은 IEEE 754 표준을 따릅니다
•
float : 32비트
◦
부호: 1비트
◦
지수: 8비트
◦
가수: 23비트
•
double : 64비트
◦
부호: 1비트
◦
지수: 11비트
◦
가수: 52비트
특수 값 처리
바이어스된 지수 값이 127인 경우 이는 부동소수점 수에서 특별한 경우, 즉 무한대나 NaN을 표현하는 데 사용됩니다.
•
지수 비트가 모두 1로 설정되고, 가수 비트가 모두 0이면 이는 무한대(Infinity)를 나타냅니다.
•
지수 비트가 모두 1로 설정되고, 가수 비트가 0이 아니면 이는 NaN을 나타냅니다.
정밀도의 한계
부동소수점 수는 이진 베이스(2진수)를 사용하여 계산되기 때문에, 일부 십진수 값은 정확하게 표현할 수 없습니다. 예를 들어, 0.1 같은 값은 이진 표현에서 정확히 나타낼 수 없어 근사값으로 처리됩니다.
float f1 = 0.1f;
float f2 = 0.1f;
Debug.Log(f1 + f2 == 0.2f); // False가 나올 수 있음
C#
복사
부동소수점 오류 다루기
이러한 정밀도의 문제를 해결하기 위해 유니티에서는 Mathf.Approximately 함수를 제공하여 두 부동소수점 수가 충분히 가까운지 비교할 수 있습니다.
if (Mathf.Approximately(f1 + f2, 0.2f)) {
Debug.Log("Values are approximately equal");
}
C#
복사
Mathf.Approximately의 작동 방식
Mathf.Approximately 함수는 다음과 같은 방식으로 두 수 a와 b가 거의 같은지를 비교합니다.
public static bool Approximately(float a, float b) {
return Mathf.Abs(b - a) < Mathf.Max(1E-06f * Mathf.Max(Mathf.Abs(a), Mathf.Abs(b)), Mathf.Epsilon * 8);
}
C#
복사
•
1E-06f * Mathf.Max(Mathf.Abs(a), Mathf.Abs(b) ): 이 표현은 두 수 중 더 큰 절대값에 0.000001을 곱한 값입니다. 즉, 두 수의 크기에 따라 허용되는 오차 범위가 동적으로 결정됩니다. 두 수가 클수록 더 큰 오차가 허용됩니다.
•
Mathf.Epsilon * 8: Mathf.Epsilon은 float에서 표현 가능한 가장 작은 값입니다. Mathf.Epsilon의 8배는 극도로 작은 값으로, 이는 매우 작은 값들 사이의 비교에서도 사용될 수 있는 최소한의 오차 한계를 제공합니다.
오차 한계
따라서, Mathf.Approximately의 오차 한계는 두 수의 절대값에 따라 변동되며, 일반적으로는 매우 작은 값 (예: 0.000001배)까지 고려합니다. 이 오차 한계는 부동소수점 연산의 정밀도 한계를 반영하여, 게임 개발 중 작은 차이로 인한 예기치 않은 동작을 방지하기 위해 설계되었습니다.