일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 암호화
- unity
- 단말기 해상도
- 이미지 폰트
- DotsTween
- C#
- base64
- Dynamic Font
- job
- Dots
- jumping ball
- adfit
- Tween
- 텍스트 메시 프로
- 2D Camera
- RSA
- 최적화
- sha
- Custom Package
- Unity Editor
- 가이드
- 프레임워크
- 커스텀 패키지
- Job 시스템
- ui
- Framework
- TextMeshPro
- 샘플
- 다이나믹 폰트
- AES
- Today
- Total
EveryDay.DevUp
[Unity] 컴퓨터의 수 표현 ( 정수, 실수 [ 고정 소수점, 부동 소수점 ] ) 본문
컴퓨터에서 처리하는 수의 표현을 알지 못하면, 실무에서 의도치 않은 실수를 할 때가 있다.
가령 공격력을 계산할 때 여러가지 float의 합과 곱으로 처리할 경우 실제 의도했던 결과 값과 다른 값을 받을 수도 있으며, if 문으로 실수 비교를 할때 true 일 것이라고 생각했던 값이 false로 나오는 경우도 있다.
private void Start()
{
float testValue = 0.0f;
for( int i = 0; i < 10; i++ )
{
testValue += 0.3f;
}
Debug.Log( "result float: " + testValue );
Debug.Log( "result double: " + (double)testValue );
Debug.Log( "result int: " + (int)testValue );
}
// result float: 3
// result double: 2.99999976158142
// result int : 2
▶ 예시 코드를 보면 testValue에 0.3을 10번 더 했기 때문에 3이 나와야 할 것 같다. float를 Console에 표시하면 3으로도 나온다. 하지만 double로 타입 캐스팅을 하면 값이 2.99999...로 나오고 심지어 int로 타입 캐스팅을 하면 2가 나온다.
( 의도 했던 값은 3이였지만, 실제 값은 2.999, int 형은 2가 나온 상황 )
▶ 변수 하나만 이렇게 오차가 나면 다행이지만, 만약 나온 결과 값을 또 다른 곳에서 곱하고 더하고 하면 점점 오차의 범위가 커질 것이다.
▣ 컴퓨터에서 수를 어떻게 표현하길래 이런 상황이 나온 것일까?
컴퓨터의 수 표현은 크게 정수와 실수로 나눌 수 있다.
정수는 -1, 1, 2, 3, 4... 을 얘기하고 실수는 -0.1, 0.2, 0.3, 1.3 ...과 같은 수를 이야기한다.
컴퓨터의 수 표현 방식을 이해하기 전에 사람의 수 표현 방식을 이해해야만 하는데 사람들은 일반적으로 10진법을 사용하여 수를 표현한다.
※ 진법이란 사람 사이의 약속과 같은 것으로 물체의 수, 크기와 같은 것을 표현한 수를 사람들이 이해할 수 있는 문자로 표현하는 방식이다.
10진법은 0~9까지의 문자를 사용하여 세상의 모든 수를 표현하는 것을 얘기한다.
0~9까지의 문자로 모든 숫자를 표현하는데에는 자릿수의 개념을 이해해야만 한다.
예를들어, '125' 이라는 숫자가 있을 때 우리는 문자 하나하나를 떼서 1 2 5 이라고 읽지않고 '백이십'이라고 읽으면서 수의 크기를 알 수 있다. 이는 사람들 사이의 약속으로써 뒤에서 부터 세번째 자리는 100 둘째자리는 20 첫째자리는 5라는 것으로 배웠기 때문이다.
'125'를 10진법의 자리수로 표현 하면 1 ×10^2, 2×10^1, 5×10^0 이 된다.
10진법에서 10^n 으로 표현되는 것은 125를 10으로 나눠보면 알 수 있다.
'125을 100으로 나눳을 때 나머지는 '25'가 되고 '25'를 10으로 나눴을 때 나머지는 5가되기 때문에 125를 10의 n 승으로 나타낼 수 있다.
사람의 10진법을 이해했다면, 컴퓨터의 수 표현을 이해해볼 차례이다.
※ 컴퓨터가 사람과 같은 10진법을 이해할 수 있다면 좋겠지만, 컴퓨터는 전기 신호로 이루어진 기계이기 때문에 전기 신호가 끊어졌다 켜졌다 두 가지 상태만을 이해할 수 있다. 끊어졌다 켜졌다는 0,1이라는 문자로 대체하여 표현할 수 있고 이를 진법으로 나타내면 2진법이 된다. 2진법은 1과 0으로 세상의 모든 수를 표현하고자하는 표현 방식이다.
사람이 이해하는 10진법과 컴퓨터가 이해하는 2진법 두 가지 진법사이에서 컴퓨터에게 사람이 명령을 내리기 위해서는 10진법을 2진법으로 변환해야할 필요가 있다.
2진법 변환 방식은 정수와 실수가 나누어져 처리되는데 먼저 10진법 정수를 2진법으로 표현하는 방식에 대해 설명하고자 한다.
▶ 정수 '125'를 2진법으로 변환하는 방식은 수를 2로 계속 나누어가면서 0이 나올때까지 나온 나머지를 아래부터 위로 적으면 된다.
125/2 = 62 나머지 1
62/2 = 31 나머지 0
31/2= 15 나머지 1
15/2=7 나머지 1
7/2=3 나머지 1
3/2=1 나머지 1
1/2=0 나머지 1
2진수 변환값은 1111101이 된다. 이 값이 맞는지 검증을 해보면 (오른쪽 끝자리 부터 2^0 으로 시작해 자리수가 증가할때마다 2^1, 2^2....으로 곱하면된다.)
1×1= 1
0×2= 0
1×4= 4
1×8= 8
1×16=16
1×32=32
1×64=64
값을 모두 더하면 125가 나옴을 알 수 있다.
정수를 2진법으로 표현하는 방법을 알았으니, 다음은 실수를 표현하는 방법에 대해 알아보고자 한다.
▶ 10진법 실수 125.2를 2진법으로 변환하는 방법은 정수와 소수를 나누어 계산하는 것이다.
실수는 정수와 소수를 나누어 변환할 수 있는데, 125는 정수로 위의 10진법 정수를 2진법으로 변환하는 방법을 사용하면 된다.
소수 0.2를 2진법으로 표현하는 방법은 2를 계속 곱해나가면서 정수가 나오는 부분을 1로 소수만 나오는 경우를 0으로 기록하는 것이다.
0.2×2=0.4 => 0
0.4×2=0.8 => 0
0.8×2=1.6 => 1
0.6×2=1.2 => 1
0.2×2=0.4 => 0
0.4×2=0.8 => 0
'0011'이라는 패턴이 반복되는것을 알 수 있다. 이 값은 0.001100110011... 무한히 반복되고 0.0011을 10진법으로 다시 변환하면 0×1/2 + 0×1/4 + 1×1/8 + 1×1/16 = 3/16 = 0.1875가 나오게된다.
만약 0.00110011로 계산하면 1/8 + 1/16+ 1/128 + 1/256 = 0.19921875로 4자리만 계산한 것보가 8자리를 계산했을 때 0.2에 좀 더 근사치가 됨을 알 수 있다.
실수는 컴퓨터에서 정확치 않은 근사값으로 계산되기 때문에 코딩을 할때 실수 처리에 대해서는 주의를 해야한다.
▶ 프로그래밍 언어에서 자료형은 수로 표현되는 값의 범위라고 할 수 있는데, 사람들이 알기 쉽게 추상화된 예약어로 대체되는 것이다.
예를 들어 bool 은 1, 0을 표현하는 것이고 string은 char[]로 char또한 언어마다 다르지만 -128~127까지 수를 표현하겠단 의미이다.
자료형은 할당된 메모리의 크기 만큼 bit에 0,1을 기록하게되고 할당된 bit의 개수에 따라 표현할 수 있는 수의 한계가 결정된다
예를 들어 정수를 char(1바이트)에 메모리를 할당해서 사용한다고 할 때 1바이트는 8비트임으로 2^8 의 수까지 원래 표현이 가능하다.
하지만 가장 앞에 비트를 부호 비트(양수와 음수) 로 사용하면서 실제로는 2^7 개의 숫자만 표현이 가는해진다. 만약 부호가 없는 unsigined char를 사용하면 2^8 까지 가능하다.
실수는 정수보다 표현할 수 있는 수의 한계가 더 작아지는데 소수를 표현하기 위해 특별한 방법을 사용하기 때문이다.
먼저 실수를 컴퓨터에서 표현하는 방식에는 고정 소수점 방식과 부동 소수점 방식이있는데 대부분 부동 소수점 방식을 사용한다고 보면된다.
고정 소수점 방식은 비트를 정수 표현을 할 수 있는 부분과 소수를 표현하는 부분을 구분지어서 사용하는 것으로 가령 8비트가 주어졌을 때 앞에 4비트는 정수를 표현 뒤의 4비트는 소수를 표현하는 것이다.
이 방식은 계산 속도가 빠르지만 표현할 수 있는 수가 제한된다는 단점을 가지고 있다
부동 소수점 방식은 실수를 정규화하여 사용하는데 실수 중 정수 한자리 1을 제외한 모든 수를 소수로 변경하는 것이다.
예를 들어 110.1100이 있다면 1.101100×2^2 으로 값을 변경하는 것입니다. 그러면 이제 옮겨진 자리수 만큼의 표현이 필요해지게 된다.
이를 위해 부동소수점방식은 특정 비트를 나누어서 의미를 부여한다.
가장 첫번째 자리는 부호(양수,음수)를 표현하고 그 뒤에는 지수를 표현하고, 그 뒤에는 가수를 넣는다.
지수 표현을 위해서는 초과 표현이라는 것을 사용하는데
예를 들어 지수 표현에 3비트를 사용한다면 원래 승의 +3을 더해서 사용하는 것이다.
3비트를 사용할 경우 -3에서 3까지의 지수 승을 표현할 수 있는데, 여기에 3을 더하면 0~6까지 지수 승을 나타낼 수 있고 이는 양수이기때문에 컴퓨터에서 단순하고 빠르게 대소 비교가 가능해진다.
실제로 위의 110.1100( 10진법 6.75 )을 8비트에 표현하면
노멀라이즈를 해서 1.101100 × 2^2 지수의 3비트 초과표현으로 +3, 2^(2+3) = 2^5
8비트에 표현하면 0 101 1101 이 된다.
계산이 맞는지 확인하면 먼저 첫번째 비트가 0임으로 양수 101은 5인데 3비트 초과표현임으로 -3 을 하면 2 1101은 1.101로 최종계산은 1.101 × 2의2승 = 110.1이되고 10진법으로는 6.5가 나오게된다.
원래 값 보다 작게 나온 이유는 표현할 수 있는 비트의 수가 실제 값보다 작기 때문이다.
실제로 프로그래밍 언어에는 최소 4바이트(32bit)를 사용하기 때문에 오차는 좀 더 작아진다.
그리고 어떻게 bit를 사용할지는 IEEE 754에서 표준을 정해두었다.
▶ 서론에서도 말했지만, 실수는 근사값으로 표현되기 때문에 항상 오차가 있을 수 있음을 알고 코드를 작성해야 한다. 정밀한 값이 필요한 경우에는 float 대신 int로 계산한 후 백분율, 만분율과 같은 것으로 가장 나중에 나눠서 사용하는 것을 추천한다.
'R&D' 카테고리의 다른 글
[Unity] 버전 관리, 형상 관리 ( SVN, GIT ) - 개념 (0) | 2020.06.06 |
---|---|
[Unity] MVC, MVP, MVVM 소프트웨어 디자인 패턴 (4) | 2020.06.01 |
[Unity] 게임 UI를 관리하기 위한 Stack 시스템 (0) | 2020.05.23 |
[Unity] 폰트 ( 이미지 Font, Dynamic Font ) (0) | 2020.05.08 |
[Unity] 게임 데이터는 어떻게 관리할 것인가. (0) | 2020.05.06 |