형변환

soomin·2021년 8월 17일
0

Java-Basic

목록 보기
10/13

모든 변수와 리터럴에는 타입이 존재하며, 리터럴은 타입이 있어야 값의 의미를 알 수 있다는 것도 배웠다.
프로그램을 작성하다보면, 서로 다른 타입 간의 연산을 진행하는 일이 있는데,
이럴 때는 연산하기 전에 두 타입을 일치시켜야만한다.

"변수"와 "상수", "리터럴"의 타입을 다른 타입으로 변환 시키는 것을 "형변환(캐스팅)"이라고 한다.



형변환의 방법


(변환하고자하는 타입) 변환되는 변수,리터럴
(int) 10.0;

캐스팅에서 사용되는 ()"캐스팅 연산자"라고 한다.

double d = 85.4;
int score = (int) d;

//해당 문장은 다음 변환과정을 거친다.
int score = (int) d;
int score = (int) 85.4; // 변수에 저장된 값 변환
int score = 85; // double 타입의 리터럴을 int 타입의 리터럴로 캐스팅
int score = 85; // int 타입 리터럴 85 를 int 타입의 변수 score에 대입

이 과정에서 중요한 점은, () 형변환 연산자기존 값을 변화시키않고 변환 된 새로운 값만을 반환하는 연산자라는 것이다.

public static void main(String[] args) {

        double d = 85.4;
        int score = (int) d;

        System.out.printf("score = %d%n",score);
        System.out.printf("d= %f",d);
        
        /**
        score = 85
        d= 85.400000
        */
    }



기본형 간의 형변환(캐스팅)

기본형은 boolean 을 제외한 "숫자로 표현할 수 있는 7개의 타입"들 간의 형변환(캐스팅)만 가능하다.
또한 기본형을 참조형으로 변환할 수 없으며, 참조형은 참조형 간의 변환만 가능하다.

//해당 코드는 박싱과 언박싱이지, 캐스팅이 아니다.
        Integer i = (Integer)10; // boxing, new Integer(10);
        int i1 = (int)i;//un-boxing, i.intValue();

이는 Integer 같은 Wrapper 들의 필드와 생성자를 열어보면 알 수 있다. String 이 내부에 charSequence 를 가진 것을 떠오르게 하는 것 같다.



정수형 간의 형 변환


정수형 간의 형변환은 표현할 수 있는 bit 의 수에 따라 달라진다.

  • 더 작은 타입이 더 큰 타입으로 변환되면, "표현할 수 있는 bit가 더 많으므로" 값의 손실이 발생하지않는다.
  • 더 큰 타입이 더 작은 타입으로 변환되면, "표현할 수 있는 bit가 더 적어지므로" 값의 손실이 발생할 수 있다.

여기서 발생할 수 있다. 는 변환되는 타입이 표현할 수 있는 값의 범위보다 크거나 작은 값의 경우에 해당한다.


int -> byte [값 손실이 발생하지 않는 경우]

int -> byte [값 손실이 발생하는 경우]


byte -> int [ 빈공간이 0으로 채워지는 경우 (양수) ]

byte -> int [ 빈공간이 1로 채워지는 경우 (음수) ]



  • 더 큰 타입이 더 작은 타입으로 캐스팅되는 경우
    • 더 큰 타입의 값이 더 작은 타입이 표현할 수 있는 범위의 값일 경우 => 값 손실이 발생하지 않음.
    • 더 큰타입의 값의 더 작은 타입이 표현할 수 있는 범위의 값보다 작거나 클경우 => 값 손실 발생.

  • 더 작은 타입이 더 큰타입으로 프로모션 되는 경우
    • 양수의 프로모션 => 빈 bit 는 0
    • 음수의 프로모션 => 빈 bit 는 1
public static void main(String[] args) {

        int i = 10;
        byte b = (byte) i;
        System.out.printf("[int -> byte] i=%d -> b=%d%n",i,b);

        i = 300;
        b = (byte) i;
        System.out.printf("[int -> byte] i=%d -> b=%d%n",i,b);

        b = 10;
        i = b;
        System.out.printf("[byte -> int] i=%d -> b=%d%n",i,b);

        b = -2;
        i = b;
        System.out.printf("[byte -> int] i=%d -> b=%d%n",i,b);

        System.out.println("i="+Integer.toBinaryString(i));
        
        /**
        [int -> byte] i=10 -> b=10
        [int -> byte] i=300 -> b=44
        [byte -> int] i=10 -> b=10
        [byte -> int] i=-2 -> b=-2
        i=11111111111111111111111111111110
        */
    }



실수형 간의 형변환


  • float 타입이 double 타입으로 형변환되면 비어있는 비트는 0 으로 채운다.
  • double 타입이 float 타입으로 형변환되면 비트를 잘라낸다..

double 타입을 float 타입으로 형변환할 때, "float 타입이 표현할 수 있는 범위보다 큰 값을 변환"하면 Infinity 가 되고, "float 타입이 표현할 수 있는 범위보다 작은 값을 변환"하면 0이 된다.

public static void main(String[] args) {

        float f = 9.1234567f;
        double d = 9.1234567;
        double d2 = (double) f;

        System.out.printf("f = %20.18f\n",f); // 정밀도를 넘어섰으므로 오차 발생
        System.out.printf("d = %20.18f\n",d); // 정밀도에 따라 값이 보존 됨
        System.out.printf("d2 = %20.18f\n",d2); // 오차가 일어난 float 리터럴을 double 로 캐스팅했으므로, 값이 정밀도에 맞게 출력
        System.out.println((float) d2); // 비트가 잘리고 반올림 발생

        /**
         * f = 9.123456954956055000
         * d = 9.123456700000000000
         * d2 = 9.123456954956055000
         * 9.123457
         */
    }



정수형과 실수형의 형변환


"정수형을 실수형으로 변환"할 경우 정수리터럴에 소수점이 붙게 되며, "실수형을 정수형으로 변환"하면 소수점 아래자리 수가 잘려나간다.

int 타입이 표현할 수 있는 자리수는 10자리(20억)이다. 하지만 float 타입의 정밀도는 7자리 이므로, 7자리가 넘는 정수를 float 타입으로 형변환하면, 오차가 발생한다.

public static void main(String[] args) {

        int i = 91234567; // 8자리 정수
        float f = (float) i; // float 타입의 정밀도 7 & 9123456오차.0f
        int i2 = (int) f;// 소수점아래는 잘려나간다. 9123456오차

        double d = (double) i; // double 타입의 정밀도 15 & 91234567.0
        int i3 = (int) d; // 91234567

        float f2 = 1.666f;
        int i4 = (int) f2; // 같은 4byte 지만 float 타입이 표현할 수 있는 범위가 더 넓으므로 캐스팅 & 1

        // 정수형을 실수형으로 형변환하면, 실수형의 정밀도만큼 정확하게 표현하고 정수 뒤에 소수점이 붙는다.
        System.out.printf("i= %d\n",i);
        System.out.printf("f= %f i2= %d\n",f,i2);
        System.out.printf("d= %f i3=%d\n",d,i3);
        System.out.printf("(int) %f = %d\n", f2, i4);
    }
    /**
    i= 91234567
    f= 91234568.000000 i2= 91234568
    d= 91234567.000000 i3=91234567
    (int) 1.666000 = 1
    */



자동 형변환


서로 다른 타입 간의 "대입" 이나 "연산"을 할 때에 형변환으로 타입을 일치시키는 것이 원칙이다.
경우에 따라 형변환 연산자를 생략할 수 있는 경우가 있는데, 이 경우는 "자동 형변환"의 경우이다.

자동 형변환은, "컴파일러가 프로그래머 대신 캐스팅 연산자를 작성해주는 것"으로, 사실 상 형변환 연산자가 기술되는 것이다.


대입의 경우, 더 작은 타입이 더 큰 타입으로 대입될 때 자동 형변환[프로모션]이 일어난다.

float f = 1234; // 4byte 로 동일하지만, float 타입이 표현할 수 있는 범위가 더 넓으므로 자동 형변환[프로모션]이 일어난다.

더 큰 타입이 더 작은 타입으로 대입될 때는 "실수가 아니라 의도적인 변환"임을 컴파일러에게 알리기 위해서 명시적 형변환[캐스팅]을 해야한다.

char ch = (char)1000;// 4byte의 int 타입리터럴이 2byte의 char 변수에 대입되므로, 명시적 형변환[캐스팅]을 해야한다.

산술 변환

연산을 하기위해서는 피연산자 간의 타입이 일치해야한다. 그러므로, 연산 전에 "자동 형변환[프로모션]"이 일어난다.
이를 "산술 변환"이라고 하며, 산술 변환은 "연산 결과를 온전히 보존" 할 수 있도록 더 넓은 범위의 타입으로 자동 형변환[프로모션]시킨 후 연산을 진행한다.

피연산자가 int 타입보다 작다면 int타입으로 타입을 일치시킨 후 연산을 진행한다.

  • byte + short -> int + int = int
  • char + short -> int + int = int

이는 JVM 의 피연산자 스택(Operand Stack)의 기본 공간이 4byte 이므로 4byte 보다 작은 타입은 int 타입으로 변환되어 연산되는 것과 관련이 있다.

피연산자가 int타입보다 크다면, 더 큰 피연산자의 타입으로 타입을 일치시킨 후 연산을 진행한다.

  • int + double -> double + double = double
  • int + float -> float + float = float
  • float + double -> double + double = double
	char a = 'A';
        byte b = 10;
        System.out.println(a+b);
        System.out.println( ((int) a) + ((int) b) );// 결과는 int 타입

        System.out.println( a + 10.5 );
        System.out.println( ((double) a) + 10.5 ); // 결과는 double 타입
        
        byte result = a + b;// X -> 결과는 int 타입이므로 명시적 형변환 없이 대입 불가능

        /**
         * 75
         * 75
         * 75.5
         * 75.5
         */

char 와 short 는 2byte 로 동일한 크기를 가지지만, 표현 범위가 다르다.

  • char = -2^16-1 ~ (2^16-1) -1
  • short = 0 ~ (2^16) -1

char 와 short 타입 간의 형변환에는 타입에 관계없이 값 손실이 발생할 수 있으므로 반드시 명시적 형변환을 진행해야한다.

 char ch = 'A';
 short s = 100;
 
 ch = (char) s;
 s = (byte) ch;
profile
블로그 유목민

0개의 댓글