저번에는 정수형에 대한 데이터 타입을 알아보았는데, 이번에는 실수입니다.
데이터 타입은 어떤 것이 있을까?
두개의 데이터 타입이 존재하는데
float, double입니다.
float는 4바이트의 공간을 차지하고, double은 8바이트의 공간을 차지합니다.
컴퓨터에서 실수는 어떻게 표현이 될까?
고정 소수점 방식
컴퓨터는 저희에게 익숙한 10진수 숫자 대신 2진수를 사용합니다. 정수는 2진수로 나타내기에 문제가 없을 것 같은데
실수는 소수점이 존재하는데 어떻게 표현할까요?
첫 번째로 3.14 와 같은 숫자가 있다면 정수인 3과 소수인 14를 나눠서 정수부, 소수부로 나누는 방식이 있을 것 같습니다.
3은 2진수로 0011, 14는 2진수로 1110 이므로 0011.1110 으로 표현할 수 있을 것입니다.
float는 4바이트의 공간을 가지고 있다고 했으니 32개의 비트를 사용할 수 있습니다.
그러면 우선 맨 앞의 1개의 비트는 부호를 나타내기 위해서(MSB) 사용하고 정수부분은 15개, 소수부분은 16개로 나눠서 정보를 저장하면 되지 않을까요?
근데 이런 방식으로 하게 되면 정수부가 나타낼 수 있는 최대의 수는 어떤 값이 나올까요? 2^15 -1 인 32767밖에 표현을 못합니다. 40000.23 이라는 값을 나타내려고 해도 할 수가 없을 것입니다. 소수는 2자리밖에 없어서 널널한데 정수는 표현을 못하니까 되게 억울한 상황입니다. 이러한 방식을 "고정 소수점 방식" 이라고 합니다. 이 방법은 필요한 곳에서는 여전히 사용하고 있다고 합니다.
부동 소수점 방식
그런데 위에서 언급한 단점으로 인해서 생겨난 방식이 있는데, 이것을 "부동 소수점"이라고 부르고 대부분의 컴퓨터는 이 방식으로 실수를 표현한다고 합니다. 여기에서 부동은 floating 둥둥 떠다닌다는 의미입니다.
우선 저희에게 익숙한 십진수를 예를 들어 보면 12.625라는 값은 1.2625 * 10^1처럼 나타낼 수 있습니다.
이와 같이 나타낼 때 1.2625는 가수 부분, 1은 지수부분, 10은 밑 수라고 합니다.
부동소수점은 float(4byte)기준으로
1비트 ( 부호를 나타내기 위한 MSB 비트 )
8비트 ( 지수를 나타내기 위한 비트 )
23비트 ( 가수를 나타내기 위한 비트 ) 를 사용합니다.
아까 고정소수점 방식에는 40000.625라는 값을 표현하려고 했다면 정수부분을 표현할 수 없었습니다. 그러나 부동소수점 방식을 사용하면 4.0000625 * 10e4로 나타내게 되고 지수가 4가 되므로 8비트로 충분히 표현이 됩니다.
컴퓨터는 2진수를 사용하기 때문에 2진수를 기준으로 생각해보면 좋을 것 같습니다.
만약 12.625라는 숫자가 있다면 우선 12 -> 1100이고 0.625는 1 * 2^-1 (0.5) + 1 * 2^-3 (0.125) 이므로 0101입니다.
위에서 40000.625 -> 4.0000625 * 10e4 로 표현하는 방법이 있었습니다.
컴퓨터는 2진수를 사용하기 때문에 이제 2진수를 기준으로 생각해보면 좋을 것 같습니다.
만약 12.625라는 숫자가 있다면 우선 12 -> 1100 이고, 0.625 는 1 * 2^-1 (0.5) + 1 * 2^-3 (0.125) 이므로 101입니다.
12.625 -> 1100.0101 입니다.
아까 40000.625 -> 4.0000625 * 10e4 로 표현하는 방법이 있었습니다. 이 방법은 가수부의 맨 앞에오는 숫자를 밑수보다 작은 숫자로 오게끔 하는 방법으로 정규화한다고 하는데,
2진수는 밑 수가 2겠죠? 그러면 0.2 를 나타내는 방식에는 0.4 * 2^-1 로 표현할 수도 있지만, 정규화를 거치면 1.6 * 2^-3 이 됩니다.
이런 식으로 값을 정규화 하고 나서 가수부분의 소수부분을 23비트 안에 집어 넣고 지수 부분은 8비트안에 집어넣는 과정을 합니다.
만약에 값이 비어있다면 0으로 가득채웁니다.
부동 소수점의 오류 (생길 수 있는 문제)
자바에서 0.1 + 1.1 == 1.2 가 성립이 할까요? false가 됩니다. 0.1 + 1.1 을 출력해보면
1.2000000000000002 와 같은 값을 얻을 수 있습니다. 이러한 일이 왜 일어날까요??
javascript도 동일한 현상이 일어나니까 지금 보시는 게시글에서 F12 또는 마우스 우측 클릭 검사 또는 command + shift + i 를 눌러서 개발자 도구 -> 콘솔 창에 가서
1.1 + 0.1 == 1.2 를 해보시면 나올 것입니다.
이것은 여러분들에게도 이미 익숙한 문제일 수 있습니다. 저희는 십진수를 생활에서 사용하죠?
1 / 3 은 무엇일까요? 0.33333333333.. 이런식으로 무한으로 퍼져나가기 때문에 보통 어느 지점에서 끊고 표현을 하게 됩니다. 그러면 (1 / 3 )+ (1 / 3) 을 하게 될때 둘째자리까지만 표현한다고 생각하면 0.33 + 0.33 = 0.66 이렇게 되는데
이것은 (1 / 3) + (1 / 3) 과 동일한 값은 아닙니다. 이 처럼 컴퓨터도 똑같은 오류가 발생합니다.
컴퓨터는 2진수를 사용하기 때문에 가장 대표적인 숫자인 0.1을 표현하려고할 때 이 값을 2진수로 표현하려고 한다면
0.0001100110011001100.. 계속해서 이런식으로 반복하게 됩니다.
그런데 이 값을 한정된 메모리 안에 저장을 해야하니까 어느 부분에서는 짤라내야합니다. float는 23자리 까지 double은 52자리 까지 표현이 되니까 그 자리에서 자르게 됩니다.
그러면 메모리에 저장된 값은 실제 그 값이 아니라 어느 정도 잘린 값이라는 것입니다. 그래서 부동 소수점에서는 0.1과 같은 값을 저장할 때 이런 오류가 있다는 것을 인지하고 사용해야합니다.
부동 소수점 오류 피하기
제가 개발하면서 이런 오류를 만난 적은 없었는데, 그 이유는 일단 저는 0.3 이라는 값을 그냥 3으로 계산해버립니다.
예를 들면 0.2초를 200ms으로 계산합니다.
다른 유튜브 댓글들을 보면 대부분 decimal을 이용한다 하시고, 다른 방법으로는 이런 오류를 피하게 해주는 라이브러리를 사용한다고 하십니다.
또는 부득이하게 소수를 사용해서 계산을 해야한다고 하면 앱실론 값을 이용해서 구분한다고 하십니다.
많이 부족한 설명이지만 봐주셔서 감사합니다. 이해하기 위해서 본 유튜브 링크입니다.
https://www.youtube.com/watch?v=wI7mVv1GYwA (뉴렉처님의 강의)
https://www.youtube.com/watch?v=-GsrYvZoAdA (코딩 애플님의 강의)
'자바' 카테고리의 다른 글
Java 데이터 타입 정리 (정수형) (1) | 2022.11.26 |
---|