야놀자 테크스쿨

Java의 자료형(리터럴과 부동소수점 방식)

chadongmin 2023. 7. 18. 18:01

자바에서 변수에 값을 대입하여 선언을 하는 경우에 오류가 나는 경우가 있습니다.

long num = 12345678900;

위와 같이 선언할때 long num이 의미하는 것은, stack 메모리에 long 타입의 8byte 크기의 num이라는 이름의 메모리 공간이 생긴다는 것을 의미합니다. 

 

고리고 12345678900이라는 수는 Operands stack이라는 기억공간에 일시적으로 적재되었다가 값이 복사되어서 num이라는 변수 메모리 공간에 저장됩니다. 

 

정수형 리터럴은 기본적으로 4byte의 공간이 할당됩니다. 그러므로 21억 즉 int의 공간이 넘어가는 수는 명시적으로 L을 작성해서 Operands stack에 8byte 공간을 할당해서 임시적으로 적재한 뒤, 변수에 값이 저장되도록 해줘야 합니다. 

 

또한 실수형 리터럴은 Operands stack에 기본적으로 double형의 크기만큼 8byte로 잡히기 때문에 float 형의 변수에 값을 할당하려고 할 땐, 접미사로 F를 붙여야 합니다. 

 

리터럴의 변환결과

부동 소수점 방식

자바에서는 실수를 표현할 때 부동소수점 방식을 사용해서 표현합니다.

부동 소수점 표현 방식

예를 들어 0.2라는 실수를 표현할 때 0.4 x 2^-1로 표현할 수 있지만, 가수의 첫번째 자리가 밑수보다 작은 '정규화'라는 과정을 거쳐서 1.6 x 2^-3으로 표현합니다. 

정규화를 하는 이유는 다음과 같습니다. 

우선 10진수를 10으로 곱하거나 나누면 소수점의 위치를 이동할 수 있습니다.

이와 마찬가지로 컴퓨터에서 사용되는 2진수 또한 2를 곱하거나 나누면 소수점의 위치를 이동할 수 있습니다.

부동 소수점 방식은 이런 원리를 이용해서 소수점의 위치를 나타냅니다.

가령 1001.1011 이라는 수가 있다면, 부동 소수점은 이 수를 1.00111011 x 2^3으로 표현합니다.

여기서 3은 소수점의 위치를 결정하게 되고 앞에 1.0011011은 1001.1011을 정규화 한 것입니다. 

 

부동 소수점 오류

부동 소수점 표현 방식의 단점은 정확성이 떨어진다는 것입니다. 정확히는 10진수를 정확하게 표현할 수 없습니다. 

정규화를 하고나서 1.m x 2^-n 식으로 표현 되는데, 2^-n이 아무리 작은 수가 되더라도 0을 표현할 수가 없기 때문입니다. 

컴퓨터는 유한한 저장공간을 가지고 있기 때문에 1/3과 같은 0.333333... 과 같은 수를 저장할 수 없습니다. 

그래서 일반적으로 32비트 저장공간 중 가수부 23비트에 무한소수점을 우겨넣고 나머지 뒷자리는 생략합니다. 

 

0.1과 같은 실수도 0.1에 가장 가까운 수를 나타낼 수 있을 뿐 정확하게는 표현하지 못합니다. 

실수를 2진수로 변환하는 방법

https://www.youtube.com/watch?v=CR2rHa9z4WE&t=186s

실수를 2진수로 변환하는 과정에서 0.1과 같은 수는 표현이 안됩니다. 

자바 BigDecimal 클래스를 사용하여 부동 소수점 오류 해결

부동 소수점을 해결하기 위해 java.math 하위에 있는 BigDecimal이라는 클래스를 사용하여 해결할 수 있습니다. 속도가 느리다는 단점이 존재하지만 숫자가 어긋날 가능성을 방지할 수 있습니다.

 

BigDecimal bigNumber1 = new BigDecimal("100000.12345");
BigDecimal bigNumber2 = new BigDecimal("10000");
				
System.out.println("덧셈(+) :" +bigNumber1.add(bigNumber2));
System.out.println("뺄셈(-) :" +bigNumber1.subtract(bigNumber2));
System.out.println("곱셈(*) :" +bigNumber1.multiply(bigNumber2));
System.out.println("나눗셈(/) :" +bigNumber1.divide(bigNumber2));
System.out.println("나머지(%) :" +bigNumber1.remainder(bigNumber2));

결과

BigDecimal을 primitive 타입으로 형변환 하는 경우

BigDecimal bigDecimal = BigDecimal.valueOf(100000.12345); //double -> BigDecimal

int int_bigNum = bigDecimal.intValue(); //BigDecimal -> int
long long_bigNum = bigDecimal.longValue(); //BigDecimal -> long
float float_bigNum = bigDecimal.floatValue(); //BigDecimal -> float
double double_bigNum = bigDecimal.doubleValue(); //BigDecimal -> double
String String_bigNum = bigDecimal.toString(); //BigDecimal -> String