본문 바로가기

Java 길찾기/이것이 자바다

[Java] 타입 변환

타입 변환이란 데이터 타입을 다른 데이터 타입으로 변환하는 것을 말한다. 예를 들어 byte 타입을 int 타입으로 변환하거나 반대로 int 타입을 byte 타입으로 변환하는 행위를 말한다. 타입 변환에는 두 가지 종류가 있다. 하나는 자동(묵시적) 타입 변환이고, 다른 하나는 강제(명시적) 타입 변환이다.

 

자동(묵시적) 타입 변환

자동 타입 변환은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말한다. 자동 타입 변환은 작은 크기를 가지는 타입이 큰 크기를 가지는 타입에 저장될 때 발생한다. 큰 크기 타입과 작은 크기 타입의 구분은 사용하는 메모리으 크기이다. 

예를 들어 byte 타입은 1byte 크기를 가지고, int 타입은 4byte 크기를 가지므로 int 타입이 큰 크기 타입이고, byte 타입이 작은  크기 타입이다. 크기별로 타입을 정리하면 다음과 같다.

byte(1) < short(2) < int(4) < long(8) < float(4) < double(8)

float은 4byte 크기인데 int 와 long 보다 더 큰 타입으로 표시했다. 그 이유는 표현할 수 있는 값의 범위가 float이 더 크기 때문이다.

 

자동 타입 변환이 발생되면 이전의 값과 변환 이후의 값은 동일하다. 정수 타입이 실수 타입으로 변환하는 것은 무조건 자동 타입 변환이 된다. 실수 타입으로 변환된 이후의 값은 정수값이 아닌 ( .0 )이 붙은 실수값이 된다. 다음 코드를 보자.

int intValue = 200;
double doubleValue = intValue;

intValue 가 doubleValue에 저장되면 200은 200.0으로 저장이 된다.

 

char 타입의 경우 int 타입으로 자동 변환되면 유니코드 값이 int 타입에 저장된다.

char charValue = 'A';
int intValue = charValue;

 

자동 타입 변환에서 단 하나의 예외가 있는데, char 는 2byte의 크기를 가지지만, char의 범위는 0 ~ 65535이므로 음수가 저장될 수가 없다. 따라서 음수가 저장될 수 있는 byte 타입을 char 타입으로 자동 변환시킬 수 없다.

byte byteValue = 65;
char charValue = byteValue; // 컴파일 에러

 

강제(명시적) 타입 변환

큰 크기의 타입은 작은 크기의 타입으로 자동 타입 변환을 할 수 없다. 예를 들어, 4byte인 int 타입을 1byte 인 byte 타입에 담을 수 없다. int 타입을 4개의 byte로 쪼갠 다음, 끝에 있는 1byte만 byte 타입 변수에 저장하는 것은 가능하다. 이와 같이 강제적으로 큰 데이터 타입을 작은 데이터 타입으로 쪼개어서 저장하는 것을 강제 타입 변환(캐스팅 : Casting)이라고 한다. 강제 타입변환은 캐스팅 연산자 ( )를 사용하는데, 괄호 안에 들어가는 타입은 쪼개는 단위이다.

int intValue = 103029770;
byte byteValue = (byte) intValue; // 강제 타입 변환 (캐스팅)

끝 1byte 만 byte 타입 변수에 담게 되므로 원래 int 값은 보존되지 않는다. 하지만 int 값이 끝 1byte 로만 표현이 가능하다면 byte 타입으로 변환해도 같은 값이 유지될 수 있다. 이럴 경우 강제 타입 변환이 의미 있게 된다. 예를 들어 int 타입 변수에 10을 저장할 경우, 4byte 중 끝 1byte로 10을 충분히 표현할 수 있으므로 앞 3byte는 모두 0으로 채워진다. 

int intValue = 10;
byte byteValue = (byte) intValue;

 

int 타입은 char 타입으로 자동 변환되지 않기 때문에 강제 타입 변환을 사용해야 한다. int 타입에 저장된 값이 유니코드 범위(0~65535)라면 다음과 같이 캐스팅 연산자를 사용해서 char 타입으로 변환할 수 있다.

int intValue = 'A';
char charValue = (char) intValue;
System.out.println(charValue);

// 출력
'A'

 

실수 타입 float, double 등은 정수 타입 byte, short, int, long 등으로 자동 변환되지 않기 때문에 강제 타입 변환을 사용해야 한다. 이 경우에는 소수점 이하 부분은 버려지고, 정수 부분만 저장된다.

double doubleValue = 3.14;
int intValue = (int) doubleValue;
System.out.println(intValue);

// 출력
3

 

강제 타입 변환에서 주의할 점은 사용자로부터 입력받은 값을 변환할 때 값의 손실이 발생하면 안된다는 것이다. 강제 타입 변환을 하기 전에 우선 안전하게 값이 보존될 수 있는지 검사하는 것이 좋다. 아래 예제는 byte 타입으로 변환하기 전에 변환될 값이 byte 타입으로 변환된 후에도 값의 손실이 발생하지 않는지 검사하여 올바른 타입 변환이 되도록 하는 코드이다.

public class CheckValueBeforeCasting {
	public static void main(String[] args) {
		int i =128;
		
		if((i<Byte.MIN_VALUE)||(i>Byte.MAX_VALUE)) {
			System.out.println("Byte 타입으로 변환할 수 없습니다.");
		}
		else {
			byte b = (byte) i;
			System.out.println(b);
		}
	}
}

 

강제 타입 변환에서 또 다른 주의점이 있다. 정수 타입을 실수 타입으로 변환할 때 정밀도 손실을 피해야 한다. 

public class FromIntToFloat {
	public static void main(String[] args) {
		int num1 = 1234567890;
		int tmp = num1;
		
		float num2 = num1;
		num1 = (int) num2;
		
		int result = tmp - num1;
		System.out.println(result);
	}
}

int 타입 변수 num1과 tmp에 동일한 1234567890 값을 저장시키고, num1을 float 타입으로 변환시킨후, 다시 int 타입으로 변환해서 num1에 저장한다. 그리고 tmp에서 num1을 뺀 결과를 result에 저장하고 콘솔에 출력한다. 동일한 값을 뺐기 때문에 당연히 0이 출력되는 것이라고 생각이 든다. 

그러나 실제로 실행해보면 0이 나오질 않는다. 그 이유는 int 값을 float 타입으로 자동 변환하면서 문제가 발생했기 때문이다. float 타입은 다음과 같이 비트 수가 할당되어 있다.

더보기

float : 부호(1bit) + 지수(8bit) + 가수(23bit)

int 값을 손실 없이 float 타입의 값으로 변환할 수있으려면 가수 23비트로 표현 가능한 값이어야 한다. 1234567890은 23비트로 표현할 수 없기 때문에 근사치로 변환된다. 즉, 정밀도 손실이 발생한다. 그렇게 때문에 float값을 다시 int 타입으로 변환하면, 원래의 int 값을 얻지 못한다. 해결책은 모든 int 값을 실수 타입으로 안전하게 변환시키는 double 타입을 사용하는 것이다. 다음은 double 타입의 할당 된 비트 수를 보여준다.

더보기

double : 부호(1bit) + 지수(11bit) + 가수(52bit)

int의 크기는 32bit 이므로 double의 가수 52bit 보다는 작기 때문에 어떠한 int 값이라도 안전하게 정밀도 손실 없이 double 타입으로 변환될 수 있다.

 

 

연산식에서의 자동 타입 변환

연산은 기본적으로 같은 타입의 피연산자(operand) 간에만 수행되기 때문에 서로 다른 타입의 피연산자가 있을 경우 두 피연산자 중 크기가 큰 타입으로 자동 변환된 후 연산을 수행한다. 예를 들어 int 타입 피연산자와 double 타입 피연산자를 덧셈 연산하면 먼저 int 타입 피연산자가 double 타입으로 자동 변환된 후에 연산을 수행한다.

int intValue = 10;
double doubleValue = 5.5;
double result = intValue + doubleValue; // result 에 15.5 저장

만약 int 타입으로 꼭 연산을 해야 한다면 double 타입을 int 타입으로 캐스팅하고 연산을 수행하면 된다.

int intValue = 10;
double doubleValue = 5.5;
double result = intValue + (int)doubleValue; // result에 15 저장

 

자바는 정수 연산일 경우 int 타입을 기본으로 한다. 크기가 4byte 보다 작은 타입들(byte, char, short)은 4byte인 int 타입으로 변환된 후 연산이 수행된다. 

예를 들어 char 타입의 연산 결과는 int 타입으로 산출되므로 int 타입 변수에 결과를 저장해야 한다. 연산의 결과를 다시 문자로 출력하거나 저장하기 위해서는 int 결과값을 char 타입으로 캐스팅 해야한다.

char ai = 'A';
int result = ai + 1;     // 'A'의 유니코드보다 1큰 유니코드가 저장
char na = (char) result; // 'B'가 저장됨

 

'Java 길찾기 > 이것이 자바다' 카테고리의 다른 글

[Java] 조건문  (0) 2021.12.29
[Java] 연산자  (0) 2021.12.28
[Java] 데이터 타입  (0) 2021.12.24
[Java] 변수란?  (0) 2021.12.23
[Java] 프로그램 개발 순서  (0) 2021.12.22