본문 바로가기

Java/기초

[Java] 기본형(Primitive type) - 정수형, 실수형

1. 정수형 - byte, short, int, long

정수형에는 모두 4개의 자료형이 있으며, 각 자료형이 저장할 수 있는 값의 범위가 서로 다르다. 

byte(1) < short(2) < int(4) : 기본 자료형(default type) < long(8)

정수형의 표현형식과 범위

어떤 진법의 리터럴을 변수에 저장해도 실제로는 2진수로 바뀌어 저장된다. 이 2진수가 저장되는 형식은 크게 정수형과 실수형이 있으며, 정수형은 다음과 같은 형식으로 저장된다.

S n-1 비트

S : 부호 비트(양수는 0, 음수는 1)

n : 타입의 크기(단위 : bit)

모든 정수형은 부호있는 정수이므로 왼쪽의 첫 번째 비트를 '부호 비트(sign bit)'로 사용하고, 나머지는 값을 표현하는데 사용한다. 그래서 n비트로 표현할 수 있는 값의 개수인 2n개에서, 절반인 '0'으로 시작하는 2n-1개의 값을 양수(0도 포함)의 표현에 사용하고, 나머지 절반인 '1'로 시작하는 2n-1개의 값은 음수의 표현에 사용한다.

정수형의 표현형식(n 비트) 종류 값의 개수
0 | n-1비트 0, 양수 2n-1
1 | n-1비트 음수 2n-1

그래서 정수형은 타입의 크기만 알면, 최대값과 최소값을 쉽게 계산해낼 수 있다.

  n비트로 표현할 수 있는 정수의 개수 : 2n개(= 2n-1 + 2n-1)

  n비트로 표현할 수 있는 부호있는 정수의 범위 : -2n-1 ~ 2n-1 -1

※ 최대값에서 1을 빼는 이유는 범위에 0이 포함되기 때문이다. 예를 들어 byte의 경우 크기가 1byte(=8bit)이므로, byte타입의 변수에 저장할 수 있는 값의 범위는 '-128~127'이다.

 

정수형의 선택기준

변수에 저장하려는 정수값의 범위에 따라 4개의 정수형 중에서 하나를 선택하면 되겠지만, byte나 short보다 int를 사용하도록 하자. byte와 short이 int보다 크기가 작아서 메모리를 조금 더 절약할 수는 있지만, 저장할 수 있는 값의 범위가 작은 편이라서 연산 시에 범위를 넘어서 잘못된 결과를 얻기가 쉽다.

그리고 JVM의 피연산자 스택(operand stack)이 피연산자를 4 byte단위로 저장하기 때문에 크기가 4 byte보다 작은 자료형(byte, shot)의 값을 계산할 때는 4byte로 변환하여 연산이 수행된다. 그래서 오히려 int를 사용하는 것이 더 효율적이다.

210 = 1024 ≒103 -> 232 = 210 X 210 X 210 X 210 = 103 X 103 X 103 X 4 = 4 X109

정수형 변수를 선언할 때는 int타입으로 하고, int의 범위(약 ±20억)를 넘어서는 수를 다뤄야할 때는 long을 사용하면 된다. 그리고 byte나 short는 성능보다 저장공간을 절약하는 것이 중요할 때 사용한다.

[참고] long타입의 범위를 벗어나는 값을 다룰 때는, 실수형 타입이나 BigInteger클래스를 사용하면 된다.

 

정수형의 오버플로우

만일 4 bit 2진수의 최대값인 '1111'에 1을 더하면 어떤 결과를 얻을까? 4bit의 범위를 넘어서는 값이 되기 때문에 에러가 발생할까?

    1111

+) 0001

------------

    ????

원래 2진수 '111'에 1을 더하면 '10000'이 되지만, 4 bit로는 4자리의 2진수만 저장할 수 있기 때문에 '000'이 된다. 즉, 5자리의 2진수 '10000'중에서 하위 4 bit만 저장하게 되는 것이다. 이처럼 연산과정에서 해당 타입이 표현할 수 있는 값의 범위를 넘어서는 것을 오버플로수(overflow)라고 한다.  오버플로우가 발생했다고 해서 에러가 발생하는 것은 아니다. 다만 예상했던 결과를 얻지 못할 뿐이다. 그래서 애초에 오버플로우가 발생하지 않게 충분한 크기의 타입을 선택해서 사용해야 한다.

10진수(4자리) 2진수(4자리)
   9999
+)      1
----------
  1 0000
   1111
+)      1
----------
 1 0000
↑저장할 공간이 없어서 1은 버려짐

오버플로우는 '자동차 주행표시기(odometer)'나, '계수기(counter)' 등 우리의 일상생활에서 발견할 수 있다. 

반대로 최솟값인 '0000' 에서 1을 감소시키면 최대값인 '9999'이 된다.

[참고] TV채널을 증가시키다가 마지막 채널에서 채널을 더 증가시키면 첫 번째 채널로 이동하고, 첫번째 채널에서 채널을 감소시키면 마지막 채널로 이동하는 것과 유사하다.

  최대값 + 1 -> 최소값

  최소값 - 1 -> 최대값

 

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

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

부호없는 10진수 2진수 부호있는 10진수
0 0000 0
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7 -> 최대값(오버플로우 발생)
8 1000 -8 -> 최소값(오버플로우 발생)
9 1001 -7
10 1010 -6
11 1011 -5
12 1100 -4
13 1101 -3
14 1110 -2
15 -> 최대값(오버플로우 발생) 1111 -1
0  -> 최소값(오버플로우 발생) 0000 0
1 0001 1
2 0010 2

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

'short'와 'char'의 크기는 모두 16 bit이므로 표현할 수 있는 값의 개수 역시 216개(65536개)로 같지만 'short'은 이중에서 절반(215개=32768개)을 '음수'를 표현하는데 사용하고, 'char'는 전체(216개=65535+1)를 '양수(65535개)와 0(1개)'을 표현하는데 사용한다.

16 bit로 표현할 수 있는 정수의 개수 : 216개(65536개)

                   short타입의 표현범위 : 215 ~ 215 -1  (-32768 ~ 32767)

                    char타입의 표현범위 : 0 ~ 216-1     (0 ~ 65535)

 

 

 

 

 

예전엔 같은 지역 내에서만 문서교환이 주를 이뤘지만, 인터넷이 발명되면서 서로 다른 지역의 다른 언어를 컴퓨터간의 문서

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

2. 실수형 - float, double

실수형의 범위와 정밀도

실수형은 실수를 저장하기 위한 타입으로 float, double 이 두 가지가 있으며 각 타입의 변수에 저장할 수 있는 값의 범위는 아래와 같다.

타입 저장 가능한 값의 범위(양수) 정밀도 크기
bit byte
float 1.4 X 10-45 ~ 3.4 X 1038 7자리 32 4
double 4.9 X 10-324 ~ 1.8 X 10308 15자리 64 8

'-'부호를 붙이면 '음의 범위'가 된다. 

float타입의 음의 범위는 '-1.4 X 10-45 ~ -3.4 X 1038'이다(0은 제외)

Q. 실수형도 정수형처럼 저장할 수 있는 범위를 넘게 되면 오버플로우가 발생하는가?

오버플로우(overflow) : 변수의 값이 표현범위를 벗어나는 것

정수형과 달리 실수형에서는 오버플로우가 발생하면 변수의 값은 '무한대(infinity)'가 된다. 실수형의 언더플로우는 표현할 수 없는 아주 작은 값, 즉 양의 최소값보다 작은 값이 되는 경우를 말한다. 이 때 변수의 값은 0이 된다.

 

4 byte의 정수로는 '약 ±2 X 109'의 값밖에 표현할 수 없는데, 어떻게 같은 4 byte로 '±3.4 X 1038'과 같은 큰 값을 표현할 수 있는 것일까? 그 이유는 바로 값을 저장하는 형식이 다르기 때문이다.

  int : S(1) + M(31) = 32(4 byte)

float : S(1)+ E(8) + M(23) = 32(4 byte)

int타입은 '부호와 값', 두 부분으로 이루어져 있지만, float타입과 같은 실수형은 '부호(S), 지수(E), 가수(M)', 세 부분으로 이루어져 있다. 즉, '10의 제곱을 곱한 형태(±M X 10E)'로 저장하기 때문에 이렇게 큰 범위의 값을 저장하는 것이 가능한 것이다.

그러나 정수형과 달리 실수형은 오차가 발생할 수 있다는 단점이 있다. 그래서 실수형에는 표현할 수 있는 값의 범위뿐만 아니라 '정밀도(precision)'도 중요한 요소이다.

float타입의 경우 정밀도가 7자리인데, 이것은 'a X 10n'(1 ≤ a ≤ 10)의 형태로 포현된 '7자리의 10진수를 오차없이 저장할 수 있다.'

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

[참고] double이라는 이름은 float보다 약 2배(double)의 정밀도를 갖는다는 의미에서 붙여진 것이다.

연산속도 향상이나 메모리를 절약하려면 float를 선택하고, 더 큰 값의 범위라던가 더 높은 정밀도를 필요로 한다면 double을 선택해야한다.

 

실수형의 저장형식

실수형의 값은 부동소수점수(floating-point)의 형태로 저장한다.

1) 부호(Sign bit)

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

2) 지수(Exponent)

'E'는 지수를 저장하는 부분으로 float의 경우, 8 bit의 저장공간을 갖는다. 지수는 '부호있는 정수'이고 8 bit로는 모두 28(=256개)의 값을 저장할 수 있으므로, '-127~128'의 값이 저장된다. 이 중에서는 -127과 128은 '숫자아님(NaN, Not a Number)'이나 '양의 무한대(POSITIVE_INFINITY)', '음의 무한대(NEGATIVE_INFINITY)'와 같이 특별한 값의 표현을 위해 예약되어 있으므로 실제로 사용가능한 지수의 범위는 '-126~127'이다.

3) 가수(Mantissa)

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

 

 

 

 

 

 

 

'Java > 기초' 카테고리의 다른 글

[Java] 배열(array)  (0) 2020.06.23
[Java] 형변환  (0) 2020.06.21
[Java] 기본형(Primitive type) - 논리형, 문자형  (0) 2020.06.14
[Java] 진법  (0) 2020.06.14
[Java] 자바 변수(Variable)  (0) 2020.06.10