C#

[C#] 부동소수점 ( + int의 float 변환)

자가라o 2021. 11. 24. 04:20

실수형 변수에는 decimal, float(single), double이 있고

그 안에서 '고정 소수점 방식'과 '부동 소수점 방식'으로 나뉩니다.

 


< 고정 소수점 (fixed point) >

 

decimal이 해당하는 방식으로

32bit(4byte) 중 [부호-1bit] [정수부-15bit] [소수부-16bit]로 이루어져 있고

이 방법이 일반적으로 생각하는 정수,소수 직접 다 저장하는 방식입니다.

따라서 표현가능한 범위가 크지 않습니다.

 

< IEEE 부동 소수점 (floating point) >

 

float(single), double이 해당하는 방식으로

부동(不動)이 아니라 부동(浮動) : '유해서 동 떠다니는 소수점'이란 뜻입니다.

이 안에서 또 분류하면 단정도 float (4byte), 그 2배인 배정도 double (8byte)로 나눕니다.

float은 4byte : [부호-1bit] [지수부-8bit] [가수부-23bit]

double은 8byte : [부호-1bit] [지수부-11bit] [가수부-52bit]

 

부동소수점 방식은 '정수 + 소수'방식이 아니라 '지수 * 가수'방식으로

숫자를 2진법으로 변환한뒤 정수부분이 1의 자리+소수점으로 남을때까지 2로 나누고 나눈만큼 지수로 곱해줍니다.

 

 

 

실수형 변수를 출력했을때 작은 숫자에 경우 정상적으로 출력하다가

float의 경우 10의 9승부터 가수E+지수 형식의 정규표현법으로 출력됩니다.

 


 

<지수부의 저장>

 

지수부는 지수에 127(바이어스)를 더하여 저장합니다.

 

바이어스(bias)란 지수의 음수 양수를 구별하기 위해 사용되는 값이며

float의 경우 0111 1111(127) 값을 더합니다.

이는 지수의 맨앞자리를 통해 지수의 부호를 나타낸다고 볼 수 있는데

127에 단 1(개의 자리수)이라도 더 해진다면 지수부의 맨앞자리는 1로 바뀌게 됩니다.

 

float으로 나타내면 4byte : [부호-1bit] [[지수부호-1bit] [지수부-7bit]] [가수부-23bit] 라고 할 수 있습니다.

바이어스는 float일때 127 / double일때 1023 입니다.

양수지수는 실수가 1의 자리보다 클때, 음수지수는 실수가 1의 자리보다 작을때 사용됩니다.

 

<가수부의 저장>

 

가수부는 변환한 숫자에서 가장 첫번째 값을 제외하고 메모리에 저장합니다.

위의 예시를 메모리에 저장한다면

 

0 1000 1100 001 1100 0100 0000 0000 0000

로 나타나겠죠? 근데 왜 첫번째를 제외할까?

 

그 이유는 맨 첫번째 값은 지수부에서 크기확인을 통해 유추할 수 있기 때문입니다.

 

실제값이 1보다 크거나 같다면 지수가 양수형태 -> 첫번째 값 1

            1보다           작다면 지수가 음수형태 -> 첫번째 값 0으로 나타나기 때문에

굳이 1bit를 더 쓸필요가 없는 것입니다.

 

▶ 부동소수점에의한 실수표현은 항상 오차가 발생한다는 특징이 있습니다.

이는 무시해도 될 정도의 수준이거나 float의 경우 double로 대체하여 정밀도를 높이는 방법으로 해결할 수 있습니다.

 


 

< 본론 : int의 float 변환 >

 

float은 최대 10의 38승까지 표현이 가능합니다.

 

float.MaxValue가 대충 340282350000000000000000000000000000000이며

int의 최대값인 약 21억과 비교했을때 굉장히 큰 숫자죠.

 

이때 제가 했던 실수는 float의 최대값이 더 크니까 int값을 float에 삽입해도 문제가 없을거라 생각한 것이었습니다.

 

float의 최대값이 int의 최대값보다는 크지만

float이 실제 나타낼 수 있는 가수부의 크기(23bit)가 int의 크기(32bit)보다 작기 때문에

 

int의 값이 대충 8,388,607을 넘어가면서 부터는 데이터의 소실이 일어나게 됩니다.

 

int max = int.MaxValue;

while (max > 10) 
{
    Console.WriteLine($"{max}");        // 실제 int값
    Console.WriteLine($"{(float)max}"); // float으로 변환
    
    max /= 10;    
    Console.WriteLine();
}

 

데이터가 손실된걸 볼 수 있습니다.

 

float -> int 에서 뿐만 아니라 int -> float에서도 데이터 손실이 일어나는걸 알 수 있습니다.

 

 

[참고]