5.1 형변환(캐스팅, casting)이란?
프로그램을 작성하다 보면 같은 타입뿐만 아니라 서로 다른 타입 간의 연산을 수행해야 하는 경우도 있다. 이럴 때는 연산을 수행하기 전에 타입을 일치시켜야 하는데,
변수나 리터럴의 타입을 다른 타입으로 변환하는 것을 ‘형변환(casting)’이라고 한다.
예를 들어 int타입의 값과 float타입의 값을 더하는 경우, 먼저 두 값을 같은 타입으로 즉, 둘 다 float타입으로 변환한 다음에 더해야 한다.
5.2 형변환 방법
형변환하고자 하는 변수나 리터럴의 앞에 변환하고자 하는 타입을 괄호와 함께 붙여준다.
(타입)피연산자
여기에 사용되는 **괄호()는 ‘캐스트 연산자’ 또는 ‘형변환 연산자’**라고 하며, 형변환을 ‘캐스팅(casting)이라고도 한다.
예를 들어 다음과 같은 코드가 있다.
double d = 85.4;
int score = (int)d; //double타입의 변수 d를 int타입으로 형변환
두 번째 줄의 연산과정을 단계별로 살펴보면,
int score = (int)d;
-> int score = (int)85.4; // 변수 d값을 읽어와서 형변환한다.
-> int score = 85; // 형변환의 결과인 85를 변수 score에 저장한다.
위 과정을 통해 형변환 연산자는 그저 피연산자의 값을 읽어서 지정된 타입으로 형변환하고 그 결과를 반환할 뿐이다. 그래서 피연산자인 변수 d의 값은 형변환 후에도 아무런 변화가 없다.
기본형(primitive type)에서 boolean을 제외한 나머지 타입들은 서로 형변환이 가능하다. 그리고 기본형과 참조형간의 형변환은 불가능하다.
float타입의 값을 int타입으로 변환할 때 소수점 이하의 값은 반올림이 아닌 버림으로 처리된다.
5.3 정수형간의 형변환
큰 타입에서 작은 타입으로의 변환, 예를 들어서 int타입(4byte)의 값을 byte타입(1byte)으로 변환하는 경우 아래와 같이 크기의 차이만큼 잘려나간다.
그래서 경우에 따라 ‘값 손실(loss of data)’이 발생할 수 있다.
반대로 작은 타입에서 큰 타입으로의 변환, 예를 들어 byte타입(1 byte)의 값을 int타입(4 byte)으로 변환하는 경우는 저장공간의 부족으로 잘려나가는 일이 값 손실이 발생하지 않는다. 그리고 나머지 공간은 0 또는 1로 채워진다.
원래 값을 채우고 남은 빈 공간은 0으로 채우는 게 보통이지만, 변환하려는 값이 음수인 경우에는 빈 공간을 1로 채운다. 그 이유는 형변환 후에도 부호를 유지할 수 있도록 하기 위해서이다.
📌
'Integer.toBinaryString(int i)'라는 메서드를 이용하면,
10진 정수를 2진 정수로 변환한 문자열을 얻을 수 있다.
5.4 실수형 간의 형변환
실수형에서도 정수형처럼 작은 타입에서 큰 타입으로 변환하는 경우, 빈 공간을 0으로 채운다.
float타입의 값을 double타입으로 변환하는 경우, 지수(E)는 float의 기저인 127을 뺀 후 double의 기저인 1023을 더해서 변환하고, 가수(M)는 float의 가수 23자리를 채우고 나머지 0으로 채운다.
double타입에서 float타입으로 변환하는 경우, 지수(E)는 double의 기저인 1023을 뺀 후 float의 기저인 127을 더하고 가수(M)는 double의 가수 52자리 중 23자리만 저장되고 나머지는 버려진다.
한 가지 주의할 점은 형변환할 때 가수의 24번째 자리에서 반올림이 발생할 수 있다는 것이다. 24번째 자리의 값이 1이면, 반올림이 발생하여 23번째 자리의 값이 1 증가한다.
5.5 정수형과 실수형 간의 형변환
정수형과 실수형은 저장형식이 완전히 다르기 때문에 정수 간의 변환처럼 간단히 값을 채우고 자르는 식으로 할 수 없다. 좀 더 복잡한 변환과정을 거쳐야 한다.
✅정수형을 실수형으로 변환
정수는 소수점이하의 값이 없으므로 비교적 변환이 간단하다. 그저 정수를 2진수로 변환한 다음 정규화를 거쳐 실수의 저장형식으로 저장될 뿐이다. 이 과정은 이미 실수형의 저장형식에서 설명했으므로 자세한 내용은 생략한다.
실수형은 정수형보다 훨씬 큰 저장범위를 갖기 때문에, 정수형을 실수형으로 변환하는 것은 별 무리가 없다. 정수를 2진수로 변환한 다음에 정규화해서 실수의 저장형식에 맞게 저장할 뿐이다.
한 가지 주의할 점은 실수형의 정밀도의 제한으로 인한 오차가 발생할 수 있다는 것이다.
예를 들어 int의 최대 값은 약 20억으로 최대 10자리의 정밀도를 요구한다. 그러나 float는 10진수로 약 7자리의 정밀도만을 제공하므로, int를 float로 변환할 때 정밀도 차이에 의한 오차가 발생할 수 있다. 그래서 10진수로 8자리 이상의 값을 실수형으로 변환할 때는 float가 아닌 double로 형변환해야 오차가 발생하지 않을 것이다.
✅ 실수형을 정수형으로 변환
실수형을 정수형으로 변환하면, 실수형의 소수점이하의 값은 버려진다. 정수형의 표현형식으로 소수점 이하의 값은 표현할 수 없기 때문이다. 예를 들어 float타입의 상수 9.1234567f를 int타입으로 형변환하면 9가 된다.
그래서 실수형을 정수형으로 형변환할 때 반올림이 발생하지 않는다. 예를 들어, 실수 1.666을 int로 형변환하면, 1이 된다.
5.6 자동 형변환
서로 다른 타입 간의 대입이나 연산을 할 때, 형변환으로 타입을 일치시키는 것이 원칙이다. 하지만, 경우에 따라 편의상의 이유로 형변환을 생략할 수 있다. 그렇다고 해서 형변환이 이루어지지 않는 것은 아니고, 컴파일러가 생략된 형변환을 자동적으로 추가한다.
float f = 1234; // 형변환의 생략
// float f = (float)1234;와 같음
위 문장에서 우변은 int타입의 상수이고, 이 값을 저장하려는 변수의 타입은 float이다. 서로 탕비이 달라서 형변환이 필요 없지만 편의상 생략하였다. float타입의 변수는 1234라는 값을 저장하는데 아무런 문제가 없기 때문이다
그러나 변수가 저장할 수 있는 값보다 더 큰 값을 저장하려는 경우에 형변환을 생략하면 에러가 난다.
byte b = 1000; /**/error byte(-128~127)**
에러 메시지는 ‘incompatible types: possible lossy conversion from int to byte’
그러나 다음과 같이 명시적으로 형변환을 해주면 형변환이 프로그래머의 실수가 아닌 의도적인 것으로 간주하고 컴파일러는 에러를 발생시키지 않는다.
char ch = (char)1000; //**명시적 형변환. 에러발생 X**
또 다른 예시로 형변환이 생략된 사례이다. 서로 다른 두 타입의 연산에서는 먼저 타입을 일치시킨 다음에 연산을 수행해야 하므로, 연산과정에서 형변환이 자동적으로 이루어진다.
int i = 3;
double d = 1.0 + i;
// **double d = 1.0 + (double)i; 에서 형변환이 생략됨**
서로 다른 두 타입 간의 덧셈에서는 두 타입 중 표현범위가 더 넓은 타입으로 형변환하여 타입을 일치시킨 다음에 연산을 수행한다. 그렇게 하는 것이 값 손실의 위험이 더 적어서 올바른 결과를 도출한다.
✅ 자동 형변환 규칙
📌 기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환한다.
보통 자료형의 크기가 큰 것일수록 값의 표현범위가 크기 마련이지만, 실수형은 정수형과 값을 표현하는 방식이 다르기 때문에 같은 크기일지라도 실수형이 정수형보다 훨씬 더 큰 표현 범위를 갖기 때문에 float와 double이 같은 크기인 int와 long보다 오른쪽에 위치한다.
char와 short은 둘 다 2 byte의 크기로 크기가 같지만, 서로 범위가 달라서 둘 중 어느 쪽으로의 형변환도 값손실이 발생할 수 있으므로 자동 형변환 X
⭐ 정리
- boolean을 제외한 나머지 7개의 기본형은 서로 형변환이 가능하다.
- 기본형과 참조형은 서로 형변환할 수 없다.
- 서로 다른 타입의 변수 간의 연산은 형변환을 하는 것이 원칙이지만
값의 범위가 작은 타입에서 큰 타입으로 형변환은 생략할 수 있다.
참고자료 -
'Java' 카테고리의 다른 글
[Java] 등가 비교 연산 (0) | 2023.05.02 |
---|---|
[Java] 연산자와 형변환 (0) | 2023.04.28 |
[Java] 문자형과 유니코드 (0) | 2023.04.13 |
[Java] 자바로 프로그램 작성하기 (0) | 2023.04.06 |
[Java] 가변배열 (0) | 2023.03.30 |