서로 다른 타입의 값끼리 연산을 해야하는 경우, 연산에 앞서 하나의 타입으로 통일시켜줄 필요가 있다. 이때 한 타입에서 다른 타입으로 자료형을 변환하는 것을 형변환이라고 한다.
형변환 하고자 하는 변수나 리터럴 앞에 원하는 타입을 괄호와 함께 붙여줌.
float exampleFloat = 36.8f;
int castedValue = (int)exampleFloat;
// castedValue는 36이라는 정수 값을 가지게 된다. (소수점 아래 버림)
기본형에서 boolean
을 제외한 나머지 타입끼리는 서로 형변환 가능.
기본형과 참조형 간 형변환은 불가능.
int exampleInt = 65;
char castedValue = (char)exampleInt;
// castedValue는 유니코드 65에 해당하는 'A'라는 문자 값을 가지게 된다.
큰 범위의 타입에서 작은 범위의 타입으로 형변환할 경우, 그 차이만큼 잘려나감. 경우에 따라 데이터 손실 발생.
int exampleInt = 300; // 2진수: 100101100
byte castedValue = (byte)exampleInt; // 2진수: 00101100
// int가 byte로 변환되면서 기존 2진수의 맨 앞자리인 1이 잘려나갔다.
반대로, 작은 범위의 타입에서 큰 범위의 타입으로 형변환하면 남는 공간은 0으로 채움. (단, 변환하는 숫자가 음수인 경우는 1로 채움)
정수형에서와 마찬가지 원리가 적용됨.
float
→ double
형변환 시, 가수 빈 공간은 0으로 채워짐.
반대로 double
→ float
형변환 시, 가수 52자리 중 23자리만 남기고 버려짐.
유의
- 가수 24번째자리가 1이면 반올림이 발생함.
float
타입의 범위를 넘어가는 경우, 최대값을 넘어가면Infinity
(무한대), 최소값을 넘어가면 0을 결과로 얻음.
정수형 → 실수형
정수형은 소수점 아래 값이 없으므로 2진수로 변환한 뒤, 정규화 과정을 거쳐 실수의 저장형식으로 저장하면 끝임.
단,
int
의 경우 최대 10자리의 정밀도를 요구하므로 7자리의 정밀도를 갖는float
으로 변환할 경우, 8자리 이상의 값을 변환하면 오차가 발생할 수 있음.
(double
로 변환하면 안전)
실수형 → 정수형
우선 실수형의 소수점 이하 값은 버려짐. 이 때 남은 정수가 정수형의 저장범위를 넘으면 오버플로우 발생.
형변환으로 타입을 통일시키지 않더라도, 컴파일러가 자동적으로 연산을 위한 형변환을 해주기도 함.
float f = 1234; // 1234 앞에 형변환식 (float)이 생략됨.
// float f = (float)1234; 와 동일함.
1234는 실수가 아닌 정수지만 값을 담는데에는 문제가 없으므로 에러가 발생하지 않음.
그러나 자료형이 가진 범위를 넘어가는 값을 담아놓고 형변환을 생략하면 데이터 손실을 유발하므로 에러 발생.
int i = 3;
double d = 4.2 + i; // i 앞에 형변환식인 (double)이 생략됨.
서로 다른 두 타입간 덧셈에서는 범위가 더 넓은 타입으로 자동적으로 형변환을 한 다음에 연산이 진행됨. (= 산술 변환)
컴파일러는 최대한 값을 보존할 수 있는 방향으로 자동 형변환을 진행함.
범위가 작은 타입부터 큰 타입 순으로 나열하면,
byte
→ short
(그리고 char
) → int
→ long
→ float
→ double
char
과short
은 2 byte로 크기가 같으나, 서로 범위 자체가 달라서 둘 중 어느쪽으로 형변환 해도 데이터 손실이 발생함. (자동 형변환 불가능)