숫자 타입
//정수
byte b = 127; //-128 ~ 127
short s = 32767; // -32,768 ~ 32,767
int i = 2147483647; //-2,147,483,648 ~ 2,147,483,647 (약 20억)
long l = 9223372036854775807L; //-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
//실수
float f = 10.0f;
double d = 10.0;
표현할 수 있는 숫자의 범위와 차지하는 메모리 공간은 다음과 같다.
- byte : -128 ~ 127 (1byte, 2⁸)
- short : -32,768 ~ 32,767 (2byte, 2¹⁶)
- int : -2,147,483,648 ~ 2,147,483,647 (약 20억) (4byte, 2³²)
- long : -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 (8byte, 2⁶⁴)
실수형
- float : 대략 -3.4E38 ~ 3.4E38, 7자리 정밀도 (4byte, 2³²)
- double : 대략 -1.7E308 ~ 1.7E308, 15자리 정밀도 (8byte, 2⁶⁴)
형변환1 - 자동 형변환(명시적 형변환)
작은 범위에서 큰 범위로의 대입은 자바 언어에서 자동으로 형 변환이 이루어진다.
큰 범위에서는 작은 범위를 표현하는데 문제가 없기 때문이다.
예) 작은 박스의 짐들을 큰 박스에 담는데는 문제가 없다.
int -> long, long -> double은 문제없이 이루어진다.
int intValue = 10;
double doubleValue = intValue; // 자동 형 변환(묵시적 형 변환)
// double doubleValue = (double) intValue; // 명시적 형 변환
형변환2 - 묵시적 형변환
반대로 큰 범위에서 작은 범위로 대입은 어떻게 될까?
불가능하다. // 자동 형변환이 이루어지지 않는다.
예) 큰 박스의 짐들을 작은 박스에 전부 담을 수 없다.
double 은 실수를 표현할 수 있지만, int는 실수를 표현할 수 없다.
double의 실수값 1.5를 int로 표현할 수는 없다.
큰 범위에서 작은 범위로의 대입은 자동 형변환이 이루어지지 않고, 아래와 같은 오류가 발생한다.
double doubleValue = 1.5;
int intValue = = doubleValue; //컴파일 오류 발생
//java: incompatible types: possible lossy conversion from double to int
//java: 호환되지 않는 유형: double에서 int로의 가능한 손실 변환
아래와 같은 방식으로 명시적 형변환이 가능하다.
double doubleValue = 1.5;
int intValue = (int) doubleValue; //형변환
그렇다면 이때 intValue의 값은 어떻게 될까?
double 타입이 아니므로 실수값 1.5를 그대로 보존할 수 없다.
int에서는 1 값이 남게 된다.
형변환과 오버플로우
형변환을 할 때 만약 작은 숫자가 표현할 수 있는 범위를 넘어서면 어떻게 될까?
int의 최댓값은 2147483647이다. 여기에 1을 더한 2147483648을 int 타입에 대입하면 어떻게 될까?
아래 테스트를 보면, -2147483648 값이 담기게 된다.
(int의 표현 범위는 -2,147,483,648 ~ 2,147,483,647 이다)
long maxIntValue = 2147483647; //int 최고값
long maxIntOver = 2147483648L; //int 최고값 + 1(초과)
int intValue = 0;
intValue = (int) maxIntValue; //형변환
System.out.println("maxIntValue casting=" + intValue); //출력:2147483647
intValue = (int) maxIntOver; //형변환
System.out.println("maxIntOver casting=" + intValue); //출력:-2147483648
결과를 확인하면 -2147483648이라는 예상치 못한 숫자가 나타납니다.
int 형은 2147483648을 표현할 수 없기 때문에, 완전히 다른 값이 나타나게 됩니다.
이처럼 허용 범위를 넘어서는 값을 표현하려 할 때, 완전히 다른 숫자가 나타나는 현상을 오버플로우라고 합니다.
계산과 형변환
형변환은 대입뿐만 아니라, 계산을 할 때도 발생한다.
int div1 = 3 / 2;
System.out.println("div1 = " + div1); //1
// int들의 계산결과 int
double div2 = 3 / 2;
System.out.println("div2 = " + div2); //1.0
// int들의 계산결과 int를 double로 자동형변환
double div3 = 3.0 / 2;
System.out.println("div3 = " + div3); //1.5
// int값 2가 double로 자동 형변환되어, 계산결과 double
double div4 = (double) 3 / 2;
System.out.println("div4 = " + div4); //1.5
// int값 2가 double로 자동 형변환되어, 계산결과 double
int a = 3;
int b = 2;
double result = (double) a / b;
System.out.println("result = " + result); //1.5
// a값이 double로 명시적형변환, 이에 따라 b가 int>double로 자동형변환되어 , 계산결과 double
자바에서 계산은 다음 2가지를 기억하자.
1. 같은 타입끼리의 계산은 같은 타입의 결과를 낸다.
- int + int는 int를, double + double 은 double의 결과가 나온다.
2. 서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다.
- int + long 은 long + long으로 자동 형변환이 일어난다.
- int + double 은 double + double로 자동 형변환이 일어난다.
정리
int -> long -> double
작은 범위에서 큰 범위로는 대입할 수 있다.
- 이것을 묵시적 형변환 또는 자동 형변환이라 한다.
큰 범위에서 작은 범위의 대입은 다음과 같은 문제가 발생할 수 있다. 이때는 명시적 형변환을 사용해야 한다.
- 소수점 버림
- 오버플로우
연산과 형변환
- 같은 타입은 같은 결과를 낸다.
- 서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다.
생각해 보자
Q1) 왜 하필 int의 최댓값(2147483647)를 1 초과하였을 때 -2147483648 라는 값이 나왔을까?
byte를 예로 들면 최대값 127은 아래와 같이 이진수로 표현될 것이다.
0 1 1 1 1 1 1 1
// 0 = 양의 부호
// 1 1 1 1 1 1 1 = 127
// 결과: +127
여기에서 해당 이진수에 값이 1 증가한다면 아래와 같이 될 것이다.
1 0 0 0 0 0 0 0
// 1 = 음의 부호
// 1 0 0 0 0 0 0 0 = 2의 보수 표현 방식에서 해당 값은 -128을 의미한다.
Q2) 왜 작은 범위에서 큰 범위로의 형 변환은 자동으로 되면서, 큰 범위에서 작은 범위로의 형변환은 자동으로 되지 않고 명시적으로 해야 하는 걸까?
이유는 당연할 듯하다. 작은 범위에서 큰 범위로 타입을 변환할 때는 실제 데이터(값)의 유실이 없기 때문에 큰 문제가 없을 것이다.
하지만 큰 범위에서 작은 범위로 타입을 변환할 때는 실제 데이터(값)의 유실 가능성이 존재한다. 그렇기 때문에 개발자에게 선택권을 넘기는 것이라고 생각한다.
예) 너 long -> int로 형 변환하면 문제가 생길 수 있는데 그래도 할 거야?
해당 내용은 아래 강의를 내용으로 작성되었습니다. (인프런 김영한 님의 자바 기초 강의)