변수 Variable

이 글은 남궁성님의 자바의 정석 3/e을 기반으로 공부한 내용을 정리한 글입니다.

진법

3.1 10진법과 2진법

대부분의 컴퓨터는 2진 체계로 설계되었기 때문에, 2진법을 알지 못하면 컴퓨터의 동작원리나 데이터 처리방식을 이해하기 어렵다. 변수에 값이 저장될 때 컴퓨터는 2진수 밖에 모르기 때문에 10진수가 2진수로 바뀌어 저장된다.

2진법은 0과 1로만 데이터를 표현하기 때문에 10진법에 비해 많은 자리수가 필요하다. 10진수 2와 같이 작은 숫자도 2진수로 표현하려면 2자리가 필요하다.

2진수
10진수
0 0
1 1
10 2
11 3
100 4
101 5
110 6
111 7
1000 8
1001 9
1010 10


3.2 비트(bit)와 바이트(byte)

‘비트(bit, binary digit)’는 한 자리의 2진수를 가리키는 말이며, 1 비트는 컴퓨터가 값을 저장할 수 있는 최소단위이다. ‘바이트(byte)’는 데이터의 기본 단위로 사용되며 1 비트 8개를 묶은 것이다.

1 bit(2개)
2 bit(4개)
3 bit(8개)
4 bit(16개)
10진수
0 00 000 0000 0
1 01 001 0001 1
  10 010 0010 2
  11 011 0011 3
    100 0100 4
    101 0101 5
    110 0110 6
    111 0111 7
      1000 8
      1001 9
      1010 10
      1011 11
      1100 12
      1101 13
      1110 14
      1111 15


이를 일반화하면, n비트로 2n개의 값을 표현할 수 있다. 그리고 n 비트로 표현가능한 10진수의 범위는 0 ~ 2n - 1이 된다.

n비트로 표현할 수 있는 10진수
값의 개수 : 2n
값의 범위 : 0 ~ 2n - 1

| 참고 | 10진수 n자리로 표현할 수 있는 값의 범위가 ‘0 ~ 10n-1’라는 것을 생각해보면 이해하기 쉽다.

3.3 8진법과 16진법

2진법은 0과 1, 두 개의 기호만으로 값을 표현하기 때문에 자리수가 길어진다는 단점이 있다. 이를 보완하기 위해 8진법이나 16진법을 사용한다.

8진수는 2진수 3자리, 16진수는 2진수 4자리를 각각 한자리로 표현할 수 있어서 자리수가 짧아져 알아보기 쉽고 서로 간의 변환방법 또한 간단하다.

2진수
8진수
10진수
16진수
0 0 0 0
1 1 1 1
10 2 2 2
11 3 3 3
100 4 4 4
101 5 5 5
110 6 6 6
111 7 7 7
1000 10 8 8
1001 11 9 9
1010 12 10 A
1011 13 11 B
1100 14 12 C
1101 15 13 D
1110 16 14 E
1111 17 15 F
10000 20 16 10


8진법은 값을 표현하는데 8개의 기호가 필요하므로 0~7의 숫자를 사용하지만, 16진법은 16개가 필요하다. 숫자만으로는 부족하므로 6개의 문자(A~F)를 추가로 사용한다.

2진수를 8진수, 16진수로 변환

2진수를 8진수로 변환하려면, 2진수를 뒤에서부터 3자리씩 끊어서 그에 해당하는 8진수로 바꾸면 된다. 8은 23이기 때문에, 8진수 한 자리가 2진수 3자리를 대신할 수 있는 것이다. 마찬가지로 16진수 변환은 4자리씩 끊어서 변환하면 된다.

3.4 정수의 진법 변환

2진수를 8진수, 16진수로 변환

10진수를 다른 진수로 변환하려면, 해당 진수로 더 이상 나눌 수 없을 때까지 나눈 뒤 몫과 나머지를 아래에서 위로 적으면 된다. n진수로 변환하려면, n으로 반복해서 나누기만 하면 되는 것이다.

n진수를 10진수로 변환

어떤 진법의 수라도 10진수로 변환하는 방법은 똑같다. 각 자리의 수에 해당 단위의 값을 곱해 모두 더하면 된다. 예를 들어 2진수 101110의 경우 각 자리의 단위가 2의 제곱이므로 이를 곱해주면 32 + 0 + 8 + 4 + 2 + 0 이므로 46이 된다.

3.5 실수의 진법 변환

10진 소수점수를 2진 소수점수로 변환하는 방법

10진 소수점수를 2진 소수점수로 변환하는 방법은 정수와 반대로 10진 소수점수에 2를 계속 곱한다. 그 결과에서 정수부만을 위에서 아래 순서대로 적고 ‘0.’을 붙이면 된다.

2진 소수점수를 10진 소수점수로 변환하는 방법

0.101(2)의 경우 각 자리 단위를 곱하면 1 x 2-1 + 0 x 2-2 + 1 x 2-3 = 1 x 0.5 + 0 x 0.25 + 1 x 0.125 = 0.625(10)이다.

3.6 음수의 2진 표현 - 2의 보수법

n비트의 2진수로 표현할 수 있는 값의 개수는 모두 2n개이므로, 4비트의 2진수로는 16개의 값을 표현할 수 있다. 이 값을 모두 ‘부호없는 정수(0과 양수)’의 표현에 사용하면 0부터 15까지의 정수를 나타낼 수 있다.

4비트의 2진수로 부호있는 정수, 즉 양수와 음수를 모두 표현할 때는 왼쪽의 첫 번째 비트(MSB)가 0이면 양수, 1이면 음수이다.

#
2진수
부호있는 10진수
1 0000 0
2 0001 1
3 0010 2
4 0011 3
5 0100 4
6 0101 5
7 0110 6
8 0111 최대값 -> 7
9 1000 -0
10 1001 -1
11 1010 -2
12 1011 -3
13 1100 -4
14 1101 -5
15 1110 -6
16 1111 최소값 -> -7


위와 같은 경우, 양수의 첫 번째 비트만 1로 바꾸면 음수가 된다는 장점이 있다. 하지만 두 수를 더했을 때 2진수로 0이 되지 않고, 0이 두 개(0, -0) 존재한다는 단점이 있다. 그리고 2진수가 증가할 때 10진 음수는 감소한다.

#
2진수
부호있는 10진수
1 0000 0
2 0001 1
3 0010 2
4 0011 3
5 0100 4
6 0101 5
7 0110 6
8 0111 최대값 -> 7
9 1000 최소값 -> -8
10 1001 -7
11 1010 -6
12 1011 -5
13 1100 -4
14 1101 -3
15 1110 -2
16 1111 -1


위와 같이 2의 보수법에 의해 음수를 배치하면, 절대값이 같은 양수와 음수를 더 했을 때 2진수로도 0의 결과를 얻으므로 부호를 신경쓰지 않고 덧셈할 수 있게 된다. 그리고 2진수가 증가할 때 10진 음수가 감소한다는 모순도 없어진다. 단, 첫 번째 비트를 바꿔서 값의 부호를 바꾸는 것은 할 수 없게 되었다.

2의 보수법

어떤 수의 ‘n의 보수’는 더했을 때 n이 되는 수를 말한다. ‘2의 보수 관계’는 더해서 2가 되는 두 수의 관계를 말하며 10진수 2는 2진수로 10이다. 2진수로 10자리올림이 발생하고 0이 되는 수를 뜻한다. 그래서 ‘2의 보수 관계’에 있는 두 2진수를 더하면 자리올림이 발생하고 0이 된다.

2진수 01011011은 서로 ‘2의 보수 관계’에 있으며, 이 두 2진수를 더하면 0이 된다.

이처럼 서로 ‘2의 보수 관계’에 있는 두 2진수로 절대값이 같고 부호가 다른 두 10진수를 표현하는 것을 2의 보수법이라고 한다.

음수를 2진수로 표현하기

10진 음의 정수를 2진수로 변환하려면, 먼저 10진 음의 정수의 절대값을 2진수로 변환한다. 그 다음에 이 2진수의 ‘2의 보수’를 구하면 된다. 예를 들어 ‘-5’의 2진 표현을 구하는 과정은 다음과 같다.

-510 --절대값--> 510 --2진수--> 0101(2) --2의보수--> 1011(2)

위 방법은 부호가 다르고 절대값이 같은 두 정수의 2진 표현이 서로 2의 보수관계에 있다는 것을 이용한 것이다.

2의 보수 구하기

‘2의 보수’의 관계에 있는 두 수를 더하면 ‘0(자리올림 발생)’이 된다. 즉, ‘2의 보수’를 구하려면 어떤 수를 더하면 0이 되는지를 알아내야 한다. 뺄셈으로 간단히 구할 수 있지만 자리수가 많아지면 뺄셈도 어려워진다. 다행히 더 간단히 구하는 방법이 있는데, 바로 ‘1의 보수’를 구한 다음 1을 더하면 ‘2의 보수’가 구해지는 것이다.

2의 보수 = 1의 보수 + 1

‘1의 보수’는 0을 1로, 1을 0으로 바꾸면 되므로 구하기가 쉽다. 예를 들어, 2진수 0101의 ‘1의 보수’는 1010이고 여기에 1을 더하면 1011로 ‘2의 보수’가 구해지는 것이다.

왜 '1의 보수 + 1'은 '2의 보수'인가?

어떤 2진수가 있을 때, 이 2진수의 1의 보수를 더하면 모든 자리가 1이 된다.

0101 + 1010 = 1111


위 식에서 양변에 1을 더하면 우변은 0이 된다. 올림(carry)이 발생하지만 4비트를 넘는 값이라 버려진다.

0101 + 1010 + 1 = 1111 + 1


0101 + ( 1010 + 1 ) = 1 0000


어떤 수에 ‘1의 보수 + 1’을 더하면 0이 되므로 ‘1의 보수 + 1’은 ‘2의 보수’와 같다는 것이 증명된다.

정리하면 다음과 같다.

음수의 2진 표현을 구하는 방법
(1) 음수의 절대값을 2진수로 변환한다.
(2) (1)에서 구한 2진수의 1을 0으로 0은 1로 바꾼다.
(3) (2)의 결과에 1을 더한다.(2의 보수 구하기, 1의 보수 + 1)

위로

기본형

4.1 논리형 - boolean

논리형에는 ‘boolean’ 한 가지 밖에 없다. boolean형 변수에는 true와 false 중 하나를 저장할 수 있으며 기본값(default)은 false이다.

boolean형 변수는 논리구현에 주로 사용된다. 그리고 두 가지의 값만을 표현하면 되므로 1 bit만으로도 충분하지만, 자바에서는 데이터 최소단위가 byte이기 때문에, boolean의 크기가 1 byte이다.

4.2 문자형 - char

문자형은 ‘char’ 한 가지 밖에 없다. 문자를 저장하기 위한 변수를 선언할 때 사용되며, char타입의 변수는 단 하나의 문자만 저장할 수 있다.

char ch = 'A';

위의 문장은 변수에 ‘문자’가 저장되는 것 같지만, 실제론 ‘문자의 유니코드(정수)’가 저장된다. 문자 ‘A’의 유니코드는 65이므로, 변수 ch에는 65가 저장된다. 그래서 문자 리터럴 대신 문자의 유니코드를 직접 저장할 수도 있다. 아래의 두 문장은 동일한 결과를 얻는다.

char ch = 'A';
char ch = 65;

char형 변수에 저장된 값을 정수형(int)으로 변환하면 유니코드를 알 수 있다.

int code = (int)ch;

특수 문자 다루기

영문자 이외에 tab이나 backspace 등의 특수문자를 저장하려면, 아래와 같은 방법을 사용한다.

char tab = '\t'; // 변수 tab에 탭 문자를 저장

‘\t’는 실제로는 두 문자로 이루어져 있지만 한 문자(탭, tab)를 의미한다.

특수 문자
문자 리터럴
tab \t
backspace \b
form feed \f
new line \n
carriage return \r
역슬래쉬() \\
작은따옴표 \’
큰따옴표 \”
유니코드(16진수)문자 \u유니코드 (예:char a = ‘\u0041’)


char타입의 표현형식

char타입의 크기는 2 byte(=16 bit)이므로, 16자리의 2진수로 표현할 수 있는 정수의 개수인 65536개(=216)의 코드를 사용할 수 있으며, char형 변수는 이 범위 내의 코드 중 하나를 저장할 수 있다.

char타입은 문자를 저장할 변수를 선언하기 위한 것이지만, 실제로 char타입의 변수에는 문자가 아닌 ‘문자의 유니코드(정수)’가 저장되고 표현형식 역시 정수형과 동일하다. 다만, 정수형과 달리 음수를 나타낼 필요가 없으므로 표현할 수 있는 값의 범위가 다르다. char타입에 저장되는 값인 유니코드는 모두 양수(0 포함)이므로, ‘0 ~ 65535’의 범위를 가진다.

인코딩과 디코딩(encoding & decoding)

컴퓨터가 숫자밖에 모르기 때문에 문자가 숫자로 변환되어 저장이 되는데 그 기준이 바로 유니코드(unicode)이다.

문자 ‘A’를 유니코드로 인코딩하면 65가 된다. 반대로 65를 유니코드로 디코딩하면 문자 ‘A’가 된다. 이처럼 문자를 코드로 변환하는 것을 ‘문자 인코딩(encoding)’, 코드를 문자로 변환하는 것을 ‘문자 디코딩(decoding)’이라고 하며, 문자를 저장할 때는 인코딩을 해서 숫자로 변환하여 저장하고 저장된 문자를 읽어올 때는 디코딩을 해서 숫자를 원래의 문자로 되돌려야 한다.

| 참고 | ‘encode’는 ‘~을 코드화하다.’ 또는 ‘~을 암호화하다.’라는 뜻이다.

4.3 정수형 - byte, shor, int, long

정수형에는 모두 4개의 자료형이 있으며, 각 자료형이 저장할 수 있는 값의 범위가 서로 다르다. 크기순으로 나열하면 다음과 같다. 단위는 byte이다.

byte < short < int < long


byte부터 long까지 1 byte부터 시작해서 2배씩 크기가 증가한다. 이 중에서도 기본 자료형(default data type)은 int이다.

정수형의 표현형식과 범위

어떤 진법의 리터럴을 변수에 저장해도 실제로는 2진수로 바뀌어 저장된다. 저장되는 형식은 크게 정수형과 실수형이 있으며, 정수형은 부호 비트 S와 타입의 크기 n - 1로 저장된다.

모든 정수형은 부호있는 정수이므로 왼쪽의 첫 번째 비트를 부호 비트(sign bit)로 사용하고, 나머지는 값을 표현하는데 사용한다. 그래서 n비트로 표현할 수 있는 값의 개수인 2n개에서, 절반인 ‘0’으로 시작하는 2n-1개의 값을 양수(0 포함)의 표현에 사용하고, 나머지 절반인 ‘1’로 시작하는 2n-1개의 값을 음수의 표현에 사용한다. 따라서 정수형은 타입의 크기만 알면 최대값과 최소값을 쉽게 계산해낼 수 있다. 범위의 최대값에서 1을 빼는 이유는 0이 포함되기 때문이다.

정수형의 표현형식과 범위

변수에 저장하려는 정수값의 범위에 따라 4개의 정수형 중에서 하나를 선택하면 되지만 int형을 사용하는 것이 좋다. byte와 short이 메모리를 조금 더 절약할 수 있지만, 저장할 수 있는 값의 범위가 작아 연산 시에 잘못된 결과를 얻을 수 있다. JVM의 피연산자 스택(operand stack)이 피연산자를 4 byte단위로 저장하기 때문에 크기가 4 byte보다 작은 자료형의 값을 계산할 때는 4 byte로 변환하여 연산이 수행된다.

정수형 변수를 선언할 때는 int타입으로 하고, int의 범위(약 ±20억)를 넘어서는 수를 다뤄야할 때는 long을 사용하면 된다.

정수형의 오버플로우

연산과정에서 해당 타입이 표현할 수 있는 값의 범위를 넘어서는 것을 오버플로우(overflow)라고 한다. 오버플로우가 발생했다고 에러가 발생하는 것은 아니고, 예상하지 못한 결과를 얻을 뿐이다. 따라서 오버플로우가 발생하지 않도록 충분한 크기의 타입을 선택해서 사용해야 한다.

정수형 타입이 표현할 수 있는 최대값에 1을 더하면 최소값이 되고, 최소값에서 1을 빼면 최대값이 된다.

부호있는 정수의 오버플로우

부호없는 정수와 부호있는 정수는 최대값과 최소값이 다르기 때문에 오버플로우가 발생하는 시점이 다르다. 부호없는 정수는 2진수로 0000이 될 때 오버플로우가 발생하고, 부호있는 정수는 부호비트가 0에서 1이 될 때 오버플로우가 발생한다.

부호없는 정수의 경우 표현범위 ‘0 ~ 15’가 반복되고, 부호있는 정수의 경우 표현범위 ‘-8 ~ 7’이 무한히 반복된다.

개수 부호 char 2진수(16bit) short 부호
65536개
(216)
0 (1개) 최소값-> 0 0000000000000000 0 0 (1개)
양수
(216-1개,
65535개)
1 0000000000000001 1 양수
(215-1개,
32767개)
2 0000000000000010 2
3 0000000000000011 3
... ... ...
32765 0111111111111101 32765
32766 0111111111111110 32766
32767 0111111111111111 32767 <- 최대값
32768 1000000000000000 -32768 <- 최소값 음수
(215,
32767개)
32769 1000000000000001 -32767
32770 0111111111111111 -32766
... ... ...
65532 0111111111111111 -4
65533 0111111111111111 -3
65534 0111111111111111 -2
최대값 -> 65535 1111111111111111 -1


4.4 실수형 - float, double

실수형의 범위와 정밀도

실수형은 실수를 저장하기 위한 타입으로 float와 double, 두 가지가 있다. float타입의 표현 범위는 -3.4x1038 ~ 3.4x1038이지만, -1.4x10-45 ~ 1.4x10-45 범위(0은 제외)의 값은 표현할 수 없다. 실수형은 소수점수도 표현해야 하므로 얼마나 큰 값을 표현할 수 있는지도 중요하지만 얼마나 0에 가깝게 표현할 수 있는지도 중요하다.

int타입은 ‘부호와 값’, 두 부분으로 이루어져 있지만, float타입과 같은 실수형은 ‘부호(S), 지수(E), 가수(M)’ 세 부분으로 이루어져 있다. 즉, 2의 제곱을 곱한 형태(±M x 2E)로 저장하기 때문에 큰 범위의 값을 저장하는 것이 가능하다. 그러나 정수형과 달리 오차가 발생할 수 있다는 단점이 있다. 그래서 값의 범위뿐만 아니라 ‘정밀도(precision)’도 중요한 요소이다.

float타입은 정밀도가 7자리인데, 이것은 ‘a x 10n‘(1≦a<10)의 형태로 표현된 7자리의 10진수를 오차없이 저장할 수 있다는 뜻이다. 만일 7자리 이상의 정밀도가 필요하다면, 변수의 타입을 double로 해야 한다. double타입은 float타입보다 정밀도가 약 2배인, 10진수로 15자리의 정밀도를 가지므로 float타입보다 훨씬 더 정밀하게 값을 표현할 수 있다.

실수형 값을 저장할 때, float타입이 아닌 double타입의 변수를 사용하는 경우는 대부분 저장하려는 값의 범위때문이 아니라 보다 높은 정밀도가 필요해서이다.

실수형의 저장형식

실수형은 값을 부동소수점수(floating-point)의 형태로 저장한다. 부동소수점수는 실수를 ‘±M x 2E‘와 같은 형태로 표현하는 것을 말하며, 부동소수점수는 부호(Sign), 지수(Exponent), 가수(Mantissa) 세 부분으로 이루어져 있다.

기호
의미
설명
S 부호(Sign bit) 0이면 양수, 1이면 음수
E 지수(Exponent) 부호있는 정수. 지수의 범위는
-127 ~ 128(float), -1023 ~ 1024(double)
M 가수(Mantissa) 실제값을 저장하는 부분.
10진수로 7자리(float), 15자리(double)의 정밀도로 저장 가능


1. 부호(Sign bit) S는 부호비트(sign bit)를 의미하며 1 bit이다. 이 값이 0이면 양수를, 1이면 음수를 의미한다. 정수형과 달리 ‘2수의 보수법’을 사용하지 않기 때문에 양의 실수를 음의 실수로 바꾸려면 부호비트만 변경하면 된다.

2. 지수(Exponent) E는 지수를 저장하는 부분으로 float의 경우, 8 bit의 저장공간을 가진다. 지수는 ‘부호있는 정수’이고 8 bit로는 256개의 값을 저장할 수 있으므로, ‘-127 ~ 128’의 값이 저장된다. 이 중에서 -127과 128은 ‘숫자 아님(NaN, Not a Number)’이나 ‘양의 무한대(POSITIVE_INFINITY)’, ‘음의 무한대(NEGATIVE_INFINITY)’와 같이 특별한 값의 표현을 위해 예약되어 있으므로 실제로 사용가능한 지수의 범위는 ‘-126 ~ 127’이다. 따라서 float타입으로 표현할 수 있는 최대값은 2127이고, 10진수로 약 1038이다. 그러나 최소값은 가수의 마지막 자리가 2-23이므로 지수의 최소값보다 2-23배나 더 작은 값, 약 10-45이다.

3. 가수(Mantissa) M은 실제 값인 가수를 저장하는 부분으로 2진수 23자리를 저장할 수 있다. 약 7자리의 10진수를 저장할 수 있는 크기인데 이것이 바로 float의 정밀도가 된다. double은 가수를 저장할 수 있는 공간이 52자리로 float의 약 2배이므로 double이 float보다 약 2배의 정밀도를 갖는 것이다.

부동소수점의 오차

실수 중에는 무한소수가 존재하므로, 정수와 달리 오차가 발생할 수 있다. 10진수가 아닌 2진수로 저장하기 때문에 10진수로는 유한소수이더라도, 2진수로 변환하면 무한소수가 되는 경우도 있다.

2진수로 변환된 실수를 저장할 때는 먼저 ‘1.xxxx x 2n‘의 형태로 변환하는데, 이 과정을 정규화라고 한다.

정규화된 2진 실수는 항상 1.로 시작하기 때문에, 1.을 제외한 23자리의 2진수가 가수(mantissa)로 저장되고 그 이후는 잘려나간다. 지수는 기저법으로 저장된다.

| 참고 | 기저법은 ‘2의 보수법’처럼 부호있는 정수를 저장하는 방법이다. 저장할 때 특정값(기저)을 더했다가 읽어올 때는 다시 뺀다.

잘려나간 값들에 의해 발생할 수 있는 최대오차는 약 2-23인데, 이 값은 가수의 마지막 비트의 단위와 같다. 2-23은 10진수로 약 10-7이므로 float의 정밀도가 7자리라고 하는 것이다.

위로

형변환

5.1 형변환(캐스팅, casting)이란?

형변환이란, 변수 또는 상수의 타입을 다른 타입으로 변환하는 것을 말한다.

5.2 형변환 방법

형변환하는 방법은 형변환하고자 하는 변수나 리터럴 앞에 변환하고자 하는 타입을 괄호와 함께 붙여주면 된다. 여기서 사용되는 괄호( )는 캐스트 연산자 또는 형변환 연산자라고 하며, 형변환을 ‘캐스팅(casting)’이라고도 한다.

형변환 연산자는 피연산자의 값을 읽어서 지정된 타입으로 형변환하고 결과를 반환한다. 즉, 피연산자의 값은 형변환 후에 아무런 변화가 없다.

기본형(primitive type)에서 boolean을 제외한 나머지 타입들은 서로 형변환이 가능하다.

5.3 정수형간의 형변환

큰 타입에서 작은 타입으로의 변환하는 경우, 값 손실(loss of data)이 발생할 수 있다. 반대의 경우, 저장공간의 부족으로 잘려나가는 일이 없으므로 값 손실이 발생하지 않는다. 나머지 빈 공간은 0 또는 1로 채워진다.

원래의 값을 채우고 남은 빈 공간은 보통 0으로 채우는데, 변환하려는 값이 음수인 경우에는 형변환 후에도 부호를 유지 할 수 있도록 빈 공간을 1로 채운다.

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 증가한다. 그리고 float 타입의 범위를 넘는 값을 float로 형변환하는 경우 ‘±무한대’ 또는 ‘&plusmn0’을 결과로 얻게 된다.

5.5 정수형과 실수형 간의 형변환

서로 간의 저장형식이 완전히 다르기 때문에 정수형간의 변환처럼 간단히 할 수 없다. 좀 더 복잡한 변환과정을 거친다.

정수형을 실수형으로 변환

정수는 소수점이하의 값이 없으므로 2진수로 변환한 다음 정규화를 거쳐 실수의 저장형식으로 저장하면 된다. 실수형은 정수형보다 훨씬 큰 저장범위를 가지기 때문에, 별 무리가 없다. 단, 실수형의 정밀도의 제한으로 인한 오차가 발생할 수 있다.

int형의 최대값은 약 20억으로 최대 10자리의 정밀도를 요구한다. 그러나 float는 10진수로 약 7자리의 정밀도만을 제공하므로, 10진수 8자리 이상의 값을 실수형으로 변환할 때는 double로 형변환해야 오차가 발생하지 않을 것이다.

실수형을 정수형으로 변환

실수형을 정수형으로 변환하면, 정수형의 표현형식으로 소수점 이하의 값은 표현할 수 없기 때문에 실수형의 소수점이하 값은 버려진다. 그래서 실수형을 정수형으로 형변환할 때 반올림이 발생하지 않는다. 만일 실수의 소수점을 버리고 남은 정수가 정수형의 저장범위를 넘는 경우에는 정수의 오버플로우가 발생한 결과를 얻는다.

5.6 자동 형변환

서로 다른 타입간의 대입이나 연산을 할 때, 형변환으로 타입을 일치시키는 것이 원칙이지만, 경우에 따라 편의상의 이유로 형변환을 생략할 수 있다. 생략된 형변환은 컴파일러가 자동적으로 추가한다. 그러나 변수가 저장할 수 있는 값의 범위보다 큰 값을 저장하려는 경우에 형변환을 생략하면 에러가 발생한다.

계산식에서 형변환이 생략되는 경우, 서로 다른 두 타입의 연산에서는 타입을 일치시킨 다음 연산을 수행해야 하므로, 자동 형변환이 이루어진다. 서로 다른 두 타입간의 덧셈에서는 값 손실의 위험이 더 적어서 올바른 결과를 얻을 확률이 높기 때문에 두 타입 중 표현범위가 더 넓은 타입으로 형변환하여 연산을 수행한다. 이러한 형변환을 ‘산술 변환’이라고 한다.

자동 형변환의 규칙

컴파일러의 자동 형변환은 기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환하는 것이다. 표현범위가 좁은 타입에서 넓은 타입으로 형변환하는 경우에는 값 손실이 없으므로 두 타입 중에서 넓은 족으로 형변환된다.

보통 자료형의 크기가 큰 것일수록 값의 표현범위가 크지만, 실수형은 정수형과 값을 표현하는 방식이 다르기 때문에 같은 크기일지라도 실수형이 정수형보다 더 큰 표현 범위를 가진다.

charshort은 둘 다 2 byte의 크기로 크기가 같지만, char의 범위는 ‘0 ~ 216-1(0 ~ 65535)’이고 short의 범위는 ‘-215 ~ 215-1(-32768 ~ 32767)’이므로 서로 범위가 달라 어느 쪽이든 값 손실이 발생할 수 있으므로 자동 형변환이 수행될 수 없다.

1. boolean을 제외한 나머지 7개의 기본형은 서로 형변환이 가능하다.
2. 기본형과 참조형은 서로 형변환할 수 없다.
3. 서로 다른 타입의 변수간의 연산은 형변환을 하는 것이 원칙이지만, 값의 범위가 작은 타입에서 큰 타입으로의 형변환은 생략할 수 있다.

위로