| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- orm
- data
- 기본형
- 티스토리챌린지
- QueryDSL
- 자격증
- aws
- Cache
- bootcamp
- 오블완
- mapping
- goorm
- spring
- Redis
- java
- Spring Boot
- backend
- Docker
- 스터디
- MSA
- jpa
- backenddeveloper
- nosql
- goorm x kakao
- serverless
- jvm
- CodeCommit
- s3
- 개발자
- 자바
- Today
- Total
gony-dev 님의 블로그
JAVA 타입 형변환 본문
지난 시간에는 JAVA 기본형 타입들에 대해 자세히 알아보았다.
이번에는 JAVA 타입들의 형변환 원리에 대해 알아보고 이를 어떻게 적용하는지 알아보도록 하겠다.
타입 변환
하나의 타입을 본인 타입 제외하고 다른 타입으로 바꾸는 것을 타입 변환 or 형변환이라고 한다.
프로그래밍 실행 동안에는 반드시 같은 타입끼리 값의 대입이나 연산을 수행할 수 있다.
그래서 같은 기본형 타입 정수형이라고 해도 타입(int, long..)이 다르면 같은 타입으로 변환해주어야하기 때문에 형변환 작업이 필요하다.
타입 변환에는 규칙이 존재한다. 메모리에 할당받은 바이트의 크기가 상대적으로 작은 타입에서 큰 타입으로의 변환은 생략할 수 있다.
하지만 메모리에 할당받은 바이트의 크기가 큰 타입에서 작은 타입으로의 변환은 데이터 손실이 발생한다.
따라서 상대적으로 바이트의 크기가 작은 타입으로 형변환을 할 경우 자바 컴파일러는 오류를 발생시킨다.
자바 형변환에는 두 가지 방법이 있다.
하나는 묵시적 형변환과, 다른 하나는 명시적 형변환이 있다.
- 묵시적 형변환 | 높은 자료형으로 변환
- 명시적 형변환 | 낮은 자료형으로 변환
묵시적 형변환
프로그램 실행 도중에 컴파일러가 자동으로 타입 변환을 일으키는 것을 의미하는 형변환이며, 자동 형변환, 묵시적 형변환, 암시적 형변환 다양한 이름으로 불린다.
단, 작은 크기의 자료형에서 큰 크기의 자료형으로 변환할 때만 자동 타입이 변환한다. 왜냐하면 자바에서는 데이터 손실이 발생하지 않거나, 데이터 손실이 최소화되는 방향으로 자동 타입 변환을 진행하기 때문이다.(ex. short -> int)

위의 플로우를 통해 이해해보자.
byte 타입은 1 byte 크기를 갖고, long 타입은 8 byte 크기를 가진다.
그래서 아래 코드처럼 구현해도 자동으로 형변환이 된다.

위의 도식화된 변환표를 보면 이상한 점이 있을 것이다.
바로 long에서 float로의 변환이다.
long 타입은 8 byte이고, float는 4 byte인데 어떻게 변환이 될까? 그 이유는 메모리 설계상 정수 타입보다 실수 타입이 더 크게 되어 있기 때문이다.
| 구분 | 타입 | 할당되는 메모리 크기 | 데이터 표현 범위 |
| 정수형 | long | 8 byte | -9,223,272,036,854,775,808 ~ 9,223,272,036,854,775,807 |
| 실수형 | float | 4 byte | (3.4 X 10^-38) ~ (3.4 X 10^38)의 근사값 |
4 byte의 float가 더 큰 수를 표현할 수 있는 가장 큰 이유는 부동소수점 방식으로 표현하기 때문이다.
부동소수점 방식에서는 지수부와 가수부를 나누고, 가수부에서는 실제 값을 지수부에는 제곱을 얼마나 곱할지 표현하기 때문에 표현 범위가 long 타입보다 더 커지게 된다.
char과 byte
위의 도식화된 표현을 보면 char과 short가 동일선상에 있는 것을 볼 수 있다.
이는 char과 short 모두 2 byte의 크기를 갖고 있기 때문이다.
또한 char 타입은 기본형 타입의 문자형이지만 아스키 코드 숫자를 저장하기에 사실상 정수형 타입이다.
아이러니하게도 1 byte인 바이트 타입은 2 byte인 char 타입으로 변환할 수가 없다. 왜냐하면 char 타입은 음수를 표현할 수 없기 때문이다.
즉, byte는 표현할 수 있는 데이터 표현 범위는 적지만 음수까지 표현하기 때문에, 음수를 표현할 수 없는 char에는 저장할 수 없다.
명시적 형변환
사용자가 타입 캐스트 연산자를 사용하여 어떤 자료형으로 선언된 변수를 다른 자료형으로 강제적으로 변환해주는 것을 의미한다.
강제 형변환으로 많이 불리우며, 묵시적 형변환과 비슷하지만 큰 데이터는 작은 데이터로 변환될 때 데이터 손실로 인해 자동 타입 변환을 할 수 없다.
강제적인 동작이기 때문에 데이터의 손실이 일어난다면, 정확한 연산은 수행할 수 없기 때문에 예상치 못할 결과를 얻을 수도 있다.
아래의 코드처럼 명시적으로 작성을 해준다면 강제 형변환이 일어나게 된다.

위의 코드처럼 기본 자료형 중 boolean을 제외하면 모든 자료형은 명시적 형변환이 가능하다.
정수 -> 실수 형변환 주의점
명시적 형변환 시 데이터 손실 외에도 정밀도 손실이라는 주의점이 존재한다.

위의 코드 과정은 다음과 같다.
- int형 변수 num1, num2 생성 후 123456789 값 대입
- float형 변수에 num2 값 대입(묵시적 형변환)
- num2에 num3 값 대입(명시적 형변환)
- num1에 num2를 뺀 값 출력
머릿속으로 생각하면 분명 0이 나와야 할텐데, 왜 -3이 나오게 된 것일까?
그 원인은 형변환에 있다.
int 값을 손실 없이 float로 변환하려면 가수 23 bit로 표현 가능한 값이어야 한다. 23 bit로 표현할 수 있는 최대값은 16,777,215이기 때문에 123,456,789는 근사치로 변환된다. 이것이 바로 정밀도 손실이다.
그래서 float 값을 다시 int 값으로 변환할 때 num2에 처음 저장하였던 본래의 값을 가져올 수 없었던 것이다.
만일 정말 가져오고 싶다면 double을 사용하여 가수부에 52비트를 할당받아 변환시키면 된다.
double과 float
면접에서 나오는 빈도 높은 질문들 중 형변환에 관한 질문이 있다.
"double형 변수 a1, a2에는 각각 1.1과 0.1이 담겨 있다. 이때 이 둘을 더했을 때 1.2이라는 결과에 대해 true인가? false인가?"
모두들 느낌상 false라는 것을 알겠지만 그 이유도 알고 있는가?
코드를 통해 알아보자.
public class MyClass {
public static void main(String[] args) {
float a1 = 1.1F;
float a2 = 0.1F;
boolean a3 = a1 + a2 == 1.2F;
System.out.println("a1과 a2의 값은 " + (a1 + a2));
System.out.println(a3 + "\n");
double b1 = 1.1;
double b2 = 0.1;
boolean b3 = b1 + b2 == 1.2;
System.out.println("b1과 b2의 값은 " + (b1 + b2));
System.out.println(b3);
}
}
/*
a1과 a2의 값은 1.2
true
b1과 b2의 값은 1.2000000000000002
false
*/
자바는 실수형 타입은 기본적으로 double로 설정되어있다.
그래서 float 변수에 소수를 대입하려면 'f'를 붙혀주거나, 명시적 형변환을 통해 double -> float로 강제 변환하여야 한다.
현재 a1과 a2의 경우에는 true가 나온다.
이는 '정확한 계산 결과다."가 아닌 "근사값이 우연히 같았다."가 맞는 표현이다.
부동소수점으로 값을 저장하는 float나 double은 10진수 소수를 이진수로 정확히 표현할 수 없어서 근사값으로 저장된다. 그래서 덧셈 결과도 근사값이 되어 정확도가 떨어지지만 이번 경우에 대해서는 연산 결과와 비교 대상이 같은 비트 대상으로 떨어져 true가 나온 것이다.
즉, 다른 값의 조합이라면 false가 나올 것이다.
double의 경우는 어떨까?
double는 가수부에 52비트를 할당받는다. 이는 가수부를 23비트 갖는 float 타입보다 정밀도가 높다는 것을 의미한다. 하지만 float 타입과 마찬가지로 10진수 소수를 정확히 표현하지 못하기 때문에 b1과 b2를 더했을 때, 1.2000000000000002가 출력되는 것을 볼 수 있다.
정리하면 float든 double이든 실수는 '정확한 결과'가 아닌 근사값이기 때문에 직접적인 비교는 가급적이면 피하고, 오차 허용 비교 개념으로 사용하는 것이 권장되는 방식이다.
출처
1- Inpa Dev
2- backendcode
'JAVA' 카테고리의 다른 글
| JAVA의 꽃, 객체 지향 프로그래밍(OOP)의 클래스 문법 (1) | 2026.01.12 |
|---|---|
| JAVA 배열을 모르겠다고? 알려드리겠습니다! (0) | 2026.01.06 |
| JAVA 기본형 데이터 타입 (0) | 2025.12.31 |
| JAVA 변수 종류 (0) | 2025.12.30 |
| 자바 소스 코드 구조 (0) | 2025.12.29 |