제어문

제어문이란 프로그램의 순차적인 흐름을 제어해야만 할 때 사용되며 조건문, 반복문 등이 있다.

제어문에 속하는 명령문들을 ({}) 중괄호로 둘러싸고 있다.

1. 조건문

주어진 조건식의 결과에 따라 별도의 명령을 수행한다.

1.1 if 문

if문은 조건식이 참(true)이면 주어진 명령문을 실행하고, 거짓(false) 면 아무것도 실행하지 않는다.

// 1. if문 기본 문법
if(condition) {
	...
}

// 2. if문 {} 생략
if(condition)
   System.out.println("오늘 입니다.");
   
// 3. if문 {} 생략시 주의
if(condition)
   System.out.println("오늘 입니다."); // if문
   System.out.println("반갑습니다."); // if문 아님

위의 예제에서 ...은 명령문이 들어가는 영역이며 if문 안에 들여 쓰기를 하여 가독성을 높인다.

  • 2번 if문의 경우 명령문의 한 줄 일 경우 중괄호를 생략할 수 있다.
  • 3번 if문의 경우 중괄호 생략은 한 줄의 명령문만 인지 함으로 "반갑습니다."는 항상 출력이 된다.

1.2 if-else 문

if문의 변형으로 if문 조건식이 거짓(false) 일 경우 else문을 실행한다. 주로 조건이 상반되는 경우에 실행되며 두 개의 if문을 합친 것과 같다.

조건이 상반되지 않을 경우 if문을 두 개 사용하면 된다.

// 1. if-else문 기본 문법
if(condition) {
	...
}else {
	...
}

// 2. if-else문 중괄호 생략
if(condition) 
	...
else 
	...

위 예제 2번처럼 if-else문도 중괄호가 생략 가능하다. 단 중괄호 생략 시 한 줄의 명령문만 인식하니 주의해야 한다.

1.3 if-else if 문

if-else if문은 처리해야 할 경우가 셋 이상인 경우 사용한다. 문법은 if-else문을 연속하여 붙인 것과 같다.

첫 번째 조건부터 차례대로 실행되면 조건이 부합하지 않으면 else문이 실행된다.

// if-else if문 기본 문법
if(condition) {
	...
} else if(condition1) {
	...
} else if(condition2) {
	...
} else {
	...
}

위 예제처럼 if-else문을 여러 개 붙인 것과 같으면 if문은 언제나 중괄호를 생략할 수 있다.

1.4 중첩 if 문

if문 안에 if문을 넣는 문법으로 중첩에 제한은 없다.

// 1. 중첩 if문 기본 문법
if(condition) {
	if(innerCondition){
    	...
    }
} else {
	...
}


// 2. 중첩 if문 중괄호 생략
if(condition) 
	if(innerCondition)
    		...
else 
	...

중첩 if문도 중괄호 생략이 가능하지만 위의 예제에서 2번의 경우 if문 안에 if-else문이 된다.

그래서 중첩 if문을 사용할 때는 괄호를 명시해주어야 한다.

1.5 switch 문

  • switch문은 조건식을 먼저 계산하고 그에 맞는 case문으로 이동한다.
  • break문을 만나면 switch문을 빠져나가지만 break문이 없다면 위에서 아래로 내려오면서 defaule문을 실행한다.
  • 경우의 수가 많아질수록 if문보다 switch문이 효율적이다.
  • switch문은 if문으로 변경 가능하지만 if문은 switch문으로 변경되지 않는 경우가 많다.
  • if-else문 보다 가독성이 좋고, 컴파일러가 최적화를 쉽게 할 수 있어 속도 또한 빠른 편이다.
  • default절은 위의 case절에 하나도 해당하지 않는다면 실행된다.
  • default절은 case절 중간에 위치해도 상관없다.
  • default절은 반드시 break문을 포함해야 한다.
// 1. switch문 기본 문법
switch(lastName) {
    case "김":
    	...
    	break;
    case "이":
    	...
    	break;
    case "박":
    	...
    case "나":    	
    	...
    default:
    	...
    	break;
}

위의 예제에서 성이 김, 이를 만나면 해당하는 case문을 실행하고 break문을 만나 switch문을 빠져나간다.

박을 만나면 박, 나, default를 실행, 나를 만나면 나, default를 실행한다.

1.5.1 switch문의 제약 조건

  1. 조건식의 결과 값은 정수 또는 문자열이 여야 한다.
  2. case의 값은 정수 상수만 가능하며, 중복되면 안 된다.

1.5.2 switch문의 중첩

// 1. switch문 중첩
switch(lastName) {
    case "김":
    	switch(firstName) {
            case "기범":
                ...
                break;
            case "태훈":
                ...
                break;
            case "동혁":
                ...
            case "승주":    	
                ...
            default:
                ...
                break;
        }
    	break;
    default:
    	...
    	break;
}

2. 반복문

반복문이란? 프로그램 내에서 똑같은 명령을 일정 횟수만큼 반복하여 수행하도록 제어하는 명령문이다.

2.1 while 문

while문은 특정 조건이 만족할 때까지 계속해서 명령문을 실행한다.

조건식이 true면 명령문을 실행하고 명령 문안에서 break를 만나지 않으면 다시 조건식으로 돌아가 명령문을 실행할지 조건식을 판단한다. break문을 만난다면 while문을 빠져나간다.

또한 continue문을 사용하면 while문의 명령문의 continue아래 로직은 실행되지 않고 조건식으로 돌아간다.

// 1. while문 기본 문법
while(condition) {
	...
}

// 2. while문 조건 추가
while(x > 0) {
	...
}

while문의 조건식을 보면 x > 0이다. 조건식으로 읽으면 x가 0보다 클 때까지(참, true) while문을 반복한다는 의미이다.

조건식이 거짓(false) 면 while문을 실행하지 않는다.

2.1.1 for문과 while문의 비교

// for문
for(int i = 0; i < 10; i++) {
	...
}

// while문
while(i < 10) {
	i++;
}

 

for문과 while문은 서로 변환이 가능하며 위와 같이 조건식이 다를 수 있다.

2.1.2 while 문의 조건식 생략 불가

while문의 조건식은 생략이 불가능하지만 무한으로 반복할 수 있게 특정 조건을 넣을 수 있다.

// while 무한 반복
while(true) {}

조건식에 true를 넣으면 while문이 무한으로 반복된다. 무한으로 반복되면 while문 이후의 로직이 진행이 되지 않아 무한루프에 빠질 수 있으니 주의해야 한다.

2.1.3 while 문 중괄호 생략

// while문 중괄호 생략
while(true)
	System.out.println("무한");

while문의 명령문이 한 줄뿐이라면 중괄호를 생략할 수 있다.

2.1.4 중첩 while 문

while문안에 while문을 넣어 중첩할 수 있으며 중첩에 제한은 없다.

// while문 중첩
while(true){
	while(true) {
    	...
    }
}

2.2 do-while 문

do-while문은 괄호를 먼저 실행하고 나서 조건식을 검사하는 while문으로 최소 한 번의 동작을 한다.

// do-while문 기본 문법
do {
	...
} while(condition);

2.3 break 문 & continue 문

break문 : 자신이 포함된 가장 가까운 반복문으로 벗어난다.

continue문 : 반복문을 끝으로 이동하여 다시 반복문을 실행한다. for 문운 증감식으로 이동, while 문은 조건식으로 이동

for(;;) {
	if(condition){
    	break; or continue;
    }
}


while(true) {
	if(condition){
    	break; or continue;
    }
}

3. for 문

while문과 달리 자체적으로 초기식, 조건식, 증감식을 모두 포함하고 있는 반복문이다.

조건식의 결과가 참(true) 일 경우 반복문을 수행한다.

// 1. for문 기본 문법
for(초기식; 조건식; 증감식) {
	...
}

// 2. for문 예제
for(int i = 0; i < 10; i++) {
	...
}
  • 초기식 : for문안에서 사용하는 변수를 선언한다. 콤마(,)로 구분하여 여러 개의 변수를 선언할 수 있다. for문이 종료되면 같이 소멸된다.
  • 조건식 : for문을 실행시킬지 검사하는 조건을 넣는다.
  • 증감식 : 조건의 범위를 증가, 감소시킨다. 콤마(,)로 구분하여 변수를 증감할 수 있다.

실행 순서로는 1. 초기화, 2. 조건식, 3. 명령문, 4. 증감식이다. 한번 반복된 이후는 1. 조건식, 2. 명령문, 3. 증감식 순서로 진행된다.

3.1 for문의 초기, 조건, 증감식과 중괄호 생략

for문의 초기, 조건, 증감식을 모두 생략할 수 있으며 생략되는 경우 무한루프가 된다.

for문의 명령문이 한 줄이라면 중괄호를 생략할 수 있다.

// 초기식, 조건식, 증감식 생략
for(;;) {
	...
}

// 중괄호 생략
for(;;) 
	...

3.2 중첩 for문

for문안에 for문을 넣어 중첩할 수 있으며 중첩에 제한은 없다.

// for문 중첩
for(;;) {
	for(;;) {
    	...
    }
}

4. 향상된 for문 (Enhanced for문)

JDK 1.5부터 Enhanced for 문이라는 반복문이 추가되었다.

컬렉션, 배열에서 유용하게 사용된다.

int[] list = {1, 2, 3, 4, 5};

for(int i : list) {
	System.out.println(i);
}

// 결과
1
2
3
4
5

위의 예제를 보면 list에 선언된 값을 하나씩 읽어온다. 그래서 값을 읽어오는 용으로만 사용할 수 있다는 제약이 있다.

'개발 언어 > Java' 카테고리의 다른 글

[Java] 객체 지향 프로그래밍  (0) 2021.08.20
[Java] 배열  (0) 2021.08.19
[Java] 연산자  (0) 2021.08.18
[Java] 형변환  (0) 2021.08.18
[Java] 실수형의 소수점 표현 방식과 BigDecimal  (0) 2021.08.13

연산자란?

연산을 수행하는 기호이다.

용어
연산자 : 연산을 수행하는 기호
피연산자 : 연산의 작업 대상 (변수, 상수, 리터럴, 수식 등)
x + 3

// x, 3 : 피연산자
// + : 연산자

1. 식(式)과 대입 연산자

식 : 연산자와 피연산자를 조합하여 계산하는 것

식의 평가 : 식을 계산하여 결과를 얻는 것

int x = 5;

1. int y = 4 * x + 3;

2. int y = 4 * 5 + 3;

3. int y = 23;

2. 연산자의 종류

연산자 종류 연산자 피연산자 결과 값 설명
산술 연산 +, -, *, /, % 이항 숫자 사칙 연산 및 나머지 계산
부호 +, - 단항 숫자 음수, 양수 부호
문자열 + 이항 문자 두 문자열을 연결
대입 연산 =, +=, -=, *=, /=, %=. &=, ^=, |=, <<=, >>=, >>>= 이항 다양 우변의 값을 좌변에 대입
증감 연산 ++, -- 단항 숫자 1씩 증가, 감소
비교 연산 ==, !=, <, >, <=, >=, instanceof 이항 boolean 값을 비교
논리 연산 !, &&, || 단항, 이항 boolean 논리적 NOT, AND, OR 연산
조건 연산 (조건식) ? A : B 삼항 다양 조건식이 true면 A, false면 B 반환
비트 ~, &, |, ^ 단항, 이항 숫자, boolean 비트 NOT, AND, OR, XOR 연산
비트 쉬프트 <<, >>, >>> 이항 숫자 비트를 좌우측으로 밀어 이동
  • 0으로 나눌 경우 ArithmeticException 에러가 발생한다.
  • 0.0으로 나눌 경우 Infinity가 출력된다.
  • byte, short의 연산의 경우 int형으로 변환하여 계산된다.
  • 나머지 연산 시 나누는 수로 음수를 허용하지만 부호는 무시된다.
char c = 'a' + 1; // 에러 없음

char c1 = 'a';
char c = c1 + 1; // 에러 발생

위의 예제에서 에러가 없는 경우는 리터럴 간 연산이기 때문에 컴파일 시 컴파일러가 값을 대체하여 준다.

하지만 아래 에러 발생의 경우 c1이라는 값이 변수라서 컴파일러가 계산을 못하여 에러가 발생한다.

그래서 char c = (char) c1 + 1;과 같이 캐스팅 연산자를 사용해야한다.

2.1 피연산자의 개수에 의한 분류

// 한개, 단항
x++;

// 두개, 이항
int x = 1 + 1;

// 세개, 삼항
int x = (a == 1) ? 1 : 2;

삼항 연산자는 괄호 안에 조건문이 true일 경우 : 기준 좌측, false일 경우 우측을 반환한다.

3. 연산자의 우선순위와 결합 규칙

연산자가 둘 이상일 경우 연산자의 우선순위에 의해 연산 순서가 결정된다.

int x = 5 + 3 * 4;

우선순위가 확실하지 않다면 괄호로 식을 묶어서 계산한다. EX) int x = (5 + 3) * 4;

우선 순위 연산자 피연산자 결합 규칙
1 (), [] 연산자 다양 -
2 증감 (++, --), 부호 (+, -), 비트 (~), 논리 (!) 단항 좌측
3 산술 (*, /, %) 이항 우측
4 산술 (+, -) 이항
5 쉬프트 (<<, >>, >>>) 이항
6 비교 (<, >, <=, >=, instanceof) 이항
7 비교 (==, !=) 이항
8 논리 & 이항(단항)
9 논리 ^ 이항(단항)
10 논리 | 이항(단항)
11 논리 && 이항
12 논리 || 이항
13 조건 ( ? : ) 삼항
14 대입 (=, +=, -=, *=, /=, %=. &=, ^=, |=, <<=, >>=, >>>=) 이항 좌측

4. 산술 변환(usual arithmetic conversion)

산술 변환이란?

연산 수행 전에 발생하는 피연산자의 자동 형 변환으로 두 피연산자의 타입을 일치시키기 위한 변환이다.

  • 작은 타입에서 큰 타입으로 변환 시 데이터 손실을 줄이기 위함
  • int 보다 작은 char, short는 int로 변환
  • 쉬프트 연산자 (<<, >>, >>>), 증감 연산자(++, --) 제외
int x = 10;
long y = 100;

// (long) x + y = 110;

위의 예시처럼 x를 long으로 변환하여 연산을 하는 것이다.

5. 비교 연산자

비교 연산자란?

두 피연산자를 비교하기 위한 연산자이다. true, false 중 하나의 결과가 나오며 타입이 서로 다른 두 피연산자는 자동 형 변환 후 비교된다.

5.1 대소 비교 연산자

연산자 : <, >, <=, >=

if(a > b) {}
if(a < b) {}
if(a <= b) {}
if(a >= b) {}

대소 비교 연산자는 기본형에 사용할 수 있으며 참조형과 boolean형에는 사용이 불가하다.

5.2 등가 비교 연산자

연산자 : ==, !=

if(a == b) {}
if(a != b) {}

등가 비교 연산자는 모든 자료형에 사용 가능하다.

float와 double을 비교하려면 서로 근사 값이 달라 double형을 float로 변환 후 비교해야 한다.

 

* 문자열 비교

문자열 비교는 ==를 사용하지 않고 equals() 함수를 사용한다.

String str1 = new String("ABC");
String str2 = "ABC";

if(str == str2) // false
if(str.equals(str2)) // true

==은 객체를 비교하여 두 개의 객체가 달라 false를 반환하지만 equals()는 내용을 비교하여 true를 반환한다.

6. 논리 연산자

논리 연산자란?

둘 이상의 조건을 AND와 OR로 연결하여 하나의 식으로 표현하는 것이다.

6.1 논리 연산자

연산자 : &&, ||, !

  • && (AND) : 두 피연산자가 모두 true일 경우 true
  • || (OR) : 두 피연산자 중 하나라도 true일 경우 true
  • ! (부정) : true -> false, false -> true
10 < x && x < 20; // x는 10보다 크고 20보다 작다
10 < x || x < 20; // x는 10보다 크거나 20보다 작다

&&가 ||보다 우선순위가 높다.

 

* 효율적인 연산

  • && (AND) 연산 시 왼쪽이 false면 우측은 평가하지 않는다.
  • || (OR) 연산 시 왼쪽이 true이면 우측은 평가하지 않는다.

6.2 비트 연산자

비트 연산자란?

피연산자를 비트 단위로 논리 연산하는 것이다.

 

연산자 : &, |, ^, ~, <<, >>

  • | (OR) : 한쪽이 1이면 1을 얻고 아니면 0을 얻는다.
  • & (AND) : 양쪽 모두 1이면 1을 얻고 아니면 0을 얻는다.
  • ^ (XOR) : 값이 서로 다를 때 1을 얻고 같으면 0을 얻는다.
X Y X | Y X & Y X ^ Y
1 1 1 1 0
1 0 1 0 1
0 1 1 0 1
0 0 0 0 0
// OR 연산자 '|'
// 주로 값을 변경할때 사용
1. OxAB | OxF => OxAF

    10101011 (OxAB)
(|) 00001111 (OxF)
    --------
        1111 (OxAF)
    
// AND 연산자 '&'
// 주로 특정 비트의 값을 뽑을때 사용
2. OxAB & OxF => OxB

    10101011 (OxAB)
(&) 00001111 (OxF)
    --------
    00001011 (OxB)

// XOR 연산자 '^'
// 같은 값을 두고 연산하면 원형으로 돌아옴
3. OxAB ^ OxF => OxA4

    10101011 (OxAB)
    00001111 (OxF)
    --------
    10100100 (OxA4)
(^) 00001111 (OxF)
    --------
    10101011 (OxAB)
  • ~ (비트 전환 연산자) : 0 -> 1, 1 -> 0으로 논리 부정 연산자와 비슷하다. 1의 보수 연산자라고도 한다.
1. (~) 00001010 (10) => 11110101 (-11)

2. (~) 00001010 (10) => 11110101 (-11) + 1 => 11110110 (-10) // 1의 보수 + 1

위의 예제 중 2번을 부면 비트 전환 연산자를 이용해 1의 보수를 구하고 구한 값에 1을 더하니 마이너스 값을 얻는 걸 알 수 있다.

(-) 부호만 붙여도 양 -> 음, 음 -> 양으로 변환을 할 수 있지만 비트 연산자를 이용하게 되면 아래와 같이 부호가 다른 값을 구할 수 있다.

 

EX) ~N+1 : 양 -> 음

     ~(N-1) : 음 -> 양

  • <<, >> (쉬프트 연산자) : 피연산자의 각 자리(2진수)를 '>>' 오른쪽, '<<' 왼쪽으로 이동(shift)하는 연산자이다.
    자리 이동으로 범위를 벗어나게 되면 0으로 채워진다. '>>' 연산자는 이동시킬 때 부호 있는 정수는 1을 부호 없는 정수는 0을 채운다.
1. 10진수 8은 2진수 00001000이다.
2. 8 << 2는 10진수 8의 2진수를 왼쪽으로 2자리 이동하라는 것이다. // 00 001000
3. 저장범위가 넘어가면 빈자리는 0으로 채운다. // 00 00100000 (32)

위의 예시 중 2번에서 연산자 우측에 있는 피연산자는 자리를 몇 번 이동할지 정하는 숫자이다.

좌측 피연산자는 산술 변환이 되어 int보다 작은 값이면 int로 변환하지만 우측 피연산자는 타입을 일치시키지 않아도 돼서 산술 변환이 일어나지 않는다.

// >> => x / 2^n의 결과

8 >> 1 -> 4
8 >> 2 -> 2

// << => x * 2^n의 결과

8 << 1 -> 16
8 << 2 -> 32

위의 예시는 이동(쉬프트) 연산자 이용하여 x / 2^n과 x * 2^n을 구하는 식이다.

이동(쉬프트) 연산자가 *, / 보다 더 빠르기는 하나 가독성이 떨어진다.

 

8 >> 32 하게 되면 int타입이 32bit여서 제자리 값이 된다. 그래서 8 >> 34는 8 >> 2와 같다.

 

7. 조건 연산자

조건 연산자는 유일한 삼항 연산자이다.

 

연산자 : ? :

// (조건문) ? 식1 : 식2
// 조건식이 true면 식1을 false면 식2를 수행한다.
x > y ? x : y;

x > 0 ? 1 : (x == 0 ? 0 : -1);

if문으로 바꿔서 사용이 가능하며 결합 규칙은 오른쪽에서 왼쪽으로 진행된다.

8. 대입 연산자

대입 연산자는 변수에 값을 대입할 때 사용하는 이항 연산자이며 결합 규칙은 오른쪽에서 왼쪽으로 진행된다.

 

연산자 : =, op=(op와=사이에 공백 없음)

연산자 설명
= 왼쪽의 피연산자에 오른쪽의 피연산자를 대입
+= 왼쪽의 피연산자에 오른쪽의 피연산자를 더한 후, 그 결과 값을 왼쪽의 피연산자에 대입
-= 왼쪽의 피연산자에 오른쪽의 피연산자를 뺀 후, 그 결과 값을 왼쪽의 피연산자에 대입
*= 왼쪽의 피연산자에 오른쪽의 피연산자를 곱한 후, 그 결과 값을 왼쪽의 피연산자에 대입
/= 왼쪽의 피연산자에 오른쪽의 피연산자로 나눈 후, 그 결과 값을 왼쪽의 피연산자에 대입
%= 왼쪽의 피연산자에 오른쪽의 피연산자로 나눈 후, 그 나머지 값을 왼쪽의 피연산자에 대입
&= 왼쪽의 피연산자를 오른쪽의 피연산자와 비트 AND 연산한 후, 그 결과 값을 왼쪽의 피연산자에 대입
|= 왼쪽의 피연산자를 오른쪽의 피연산자와 비트 OR 연산한 후, 그 결과 값을 왼쪽의 피연산자에 대입
^= 왼쪽의 피연산자를 오른쪽의 피연산자와 비트 XOR 연산한 후, 그 결과 값을 왼쪽의 피연산자에 대입
<<= 왼쪽의 피연산자를 오른쪽의 피연산자만큼 왼쪽 이동 후, 그 결과 값을 왼쪽의 피연산자에 대입
>>= 왼쪽의 피연산자를 오른쪽의 피연산자만큼 부호를 유지하며 오른쪽 이동 후, 그 결과 값을 왼쪽의 피연산자에 대입
>>>= 왼쪽의 피연산자를 오른쪽의 피연산자만큼 부호에 상관없이 오른쪽 이동 후, 그 결과 값을 왼쪽의 피연산자에 대입
n = 5 // 대입
n += 5 // 더한 후 대입
n -= 5 // 뺀 후 대입
n *= 5 // 곱한 후 대입
n /= 5 // 나눈 후 대입
n %= 5 // 나눈 후 나머지 대입
n &= 5 // AND 후 대입
n |= 5 // OR 후 대입
n ^= 5 // XOR 후 대입
n <<= 5 // 왼쪽으로 이동 후 대입
n >>= 5 // 부호 유지하면 오른쪽으로 이동 후 대입
n >>>= -5 // 부호 상관 없이 오른쪽으로 이동 후 대입

참조

https://kephilab.tistory.com/28

 

4. Java 자바 - 연산자 종류, 연산자 우선순위

1. 연산자 종류 연산자 종류 연산자 피연산자 수 산출값 설명 산술 연산 +, -, *, /, % 이항 숫자 사칙연산 및 나머지계산 한다. 부호 +, - 단항 숫자 음수 / 양수 부호 문자열 + 이항 문자 두 문자를 연

kephilab.tistory.com

https://tcpschool.com/java/java_operator_assignment

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

 

'개발 언어 > Java' 카테고리의 다른 글

[Java] 배열  (0) 2021.08.19
[Java] 제어문  (0) 2021.08.18
[Java] 형변환  (0) 2021.08.18
[Java] 실수형의 소수점 표현 방식과 BigDecimal  (0) 2021.08.13
[Java] 오버 플로우, 언더 플로우  (0) 2021.08.12

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

변수나 리터럴의 타입을 다른 타입으로 변환하는 것을 형 변환이라고 한다.

형 변환 방법

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

위와 같이 형 변환 연산자 '()'를 이용하여 변환한다.

변환 과정

double d = 85.4;

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

기본형의 형 변환

char c = (char) 65; // int -> char
int i = (int) 'A'; // char -> int
int i = (int) 1.6f; // float -> int
float f = (float) 10; // int -> float

boolean타입은 다른 타입과 형 변환이 안된다.

정수형 간의 형 변환

// 큰 타입 -> 작은 타입
byte b = (byte) 10; // 1010(10) -> 1010(10)
byte b = (byte) 300; // 100101100(300) -> 00101100(44)

// 작은 타입 -> 큰 타입
int i = (int) 10; // 1010(10) -> 1010(10)
int i = (int) -2; // 11111110(-2) -> 111111100(-2)
  • 작은 타입으로 변환할 경우 값의 손실이 발생할 수 있다.
  • 큰 타입으로 변환할 경우 값의 손실이 없다.
  • 변환하고자 하는 값이 양수면 0, 음수면 1을 빈칸에 채워 넣는다.

실수형 간의 형 변환

작은 타입 -> 큰 타입 (float -> double)

  • float의 기저인 127을 빼고 double의 기저인 1023을 더한다.
  • float의 가수 23자리를 채우고 빈칸에 0을 채운다.

큰 타입 -> 자은 타입 (double -> float)

  • double의 기저인 1023을 빼고 float의 기저인 127을 더한다.
  • double의 가수 52자리 중 23자리만 저장하고 나머지는 버린다.

주의점)

  1. 24번째 자리가 1일 경우 반올림이 발생한다.
  2. float 타입의 값을 넘는 경우 ±무한대(큰 경우), ±0(작은 경우)의 결과를 얻는다.

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

저장 방식

정수형의 저장 방식
실수형의 저장 방식

정수형 -> 실수형 변환

10진수 7 (0 | 000...00111) 111 -> 1.11 * 2^2+127 (정규화) (0 | 10000001 | 11000...000)

 

주의점)

int의 최대 값인 약 20억을 변환할 경우 최대 10자리의 정밀도가 필요하다. 큰 값을 변환할 경우 deouble형으로 변환해야 오차가 발생하지 않는다.

 

EX)

int -> float -> int : 9123456 -> 91234568.0 -> 91234568

int -> double -> int : 9123456 -> 91234567.0 -> 91234567

실수형 -> 정수형 변환

정수형은 소수점 이하를 표현할 수 없기 때문에 소수점 이하의 값은 버려진다.

EX)

9.1234567f -> 9

 

float (0 | 10000010 | 001000111111001101011101) -> 1001.001000111111001101011101

int (0 | 0000000....00001001)

 

위의 예시처럼 소수점은 모두 버려진다.

만약 정수로 변환 시 정수의 범위를 넘는다면 오버플로우가 발생한다.

자동 형 변환

  1. 편의상의 이유로 형 변환을 생략할수 있다. 
  2. 형변환을 명시 해주면 의도적인 형 변환으로 간주하여 에러가 발생하지 않는다. 
  3. 서로 다른 타입의 덧셈의 경우 더 큰 범위를 가진 타입으로 자동 형 변환이 이루어져 계산되어 값 손실을 줄이고 올바른 값을 얻는다.

연산자의 범위보다 큰 값을 저장하는 경우 에러가 발생한다. EX) incompatible types: possible lossy conversion from int to byte

 

아래의 예시는 위의 3가지 특징을 보여주는 예시이다.

1. float f = 1234;
2. char c = (char) 100;
3. int i = 3;
   double d = 1.0 + i;
   // double d = 1.0 + (double) i;

자동으로 형 변환하는 것을 산술 변환이라고 한다.

자동 형 변환의 규칙

  • 기존의 값을 최대한 보존할 수 있는 타입으로 자동 변환
  • 표현 범위가 넓은 쪽으로 변환하여 값 손실을 줄인다.
  • char와 short는 같은 2byte지만 값의 범위가 달라 자동 형 변환이 될 수 없다.

자동 형 변환의 타입 나열

// 1byte => 2byte => 4byte => 8byte => 4byte => 8byte

byte  => short => int   => long  => float => double
	  char =>

위의 순서대로 변환 시 자동 변환이 된다.

반대 방향으로 변환시 캐스팅 연산자를 써줘야 한다.

형 변환의 정리

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

'개발 언어 > Java' 카테고리의 다른 글

[Java] 제어문  (0) 2021.08.18
[Java] 연산자  (0) 2021.08.18
[Java] 실수형의 소수점 표현 방식과 BigDecimal  (0) 2021.08.13
[Java] 오버 플로우, 언더 플로우  (0) 2021.08.12
[Java] 상수와 리터럴  (0) 2021.08.11

실수형의 종류

  범위 bit byte
float 1.4E-45 ~ 3.4E38 32 4
double 4.9E-324 ~ 1.8E308 64 8

실수형의 저장 형식

  부호 (Sign) 지수 (Exponent) 가수 (Mantissa)
float 1 8 23
double 1 11 52

소수 점의 표현 방식

  • 소수 점의 표현 방식은 두 가지가 있으며 두 가지 방식은 고정 소수점 방식부동 소수점 방식이다.
  • 컴퓨터는 정수와 마찬가지로 실수를 2진수로 표현해야 하기 때문에 실수를 표현하는 것이 정수를 표현하는 것보다 많이 복잡하다.
  • 10진수의 유한 소수가 2진수로 무한 소수가 될수 있어 2진수로 10진수를 정확하게 표현하기 어렵고 버려지는 값들로 인해 오차가 발생한다.

고정 소수점 방식

  • 부호 : 0이면 양수, 1이면 음수

각 위치마다 2진수가 저장되기 때문에 표현할수 있는 범위가 제한적이다.

총 32bit로 표현 가능한 실수의 범위와 정밀도가 제한적이어서 잘 사용하지 않는다.

 

부동 소수점 방식

  • 부호 (Sign) : 0이면 양수, 1이면 음수
  • 지수부 (Exponent) : 소수점의 위치를 나타낸다.
  • 가수부 (Mantissa) : 양의 정수를 표현한다.
부동 소수점은 IEE 754 표준 방식을 따르고 있다.

부동 소수점 방식의 각 위치의 bit는 타입에 맞는 실수형 bit와 동일하다.

고정 소수점 방식보다 넓은 범위와 높은 정밀도를 가지고 있기 때문에 많은 시스템에서 부동 소수점 방식을 사용한다.

부동 소수점 방식의 오차

double num = 0.1;

for(int i = 0; i < 1000; i++) {
	num += 0.1;
}

System.out.println(num);

위의 예상 결과가 100으로 예상이 되지만 실제로는 100.09999999999859가 출력된다.

이처럼 컴퓨터가 연산하는 모든 실수형 연산은 작은 오차가 있다.이런 경우 작은 오차에도 민감한 금융 업계에서는 이 오차가 큰 영향을 미칠 수 있다.

부동 소수점 방식의 오차 해결 방법

해결 방법은 BigDecimal 클래스를 사용하는 것이다.

 

BigDecimal의 특성

  • BigDecimal은 실수형과 달리 정수를 이용하여 실수를 표현한다.
  • 오차가 없는 2진수 정수로 변환하여 다룬다.
  • 실수를 정수와 10의 제곱의곱으로 표현한다. ex) 정수 * 10^-scale
  • 정수를 저장하기 위해 BigInteger를 사용한다.

BigDecimal의 생성

BigDecimal bd = new BigDecimal("123.456789"); // 문자열 리터럴
BigDecimal bd = new BigDecimal(123.456789); // 실수형 리터럴
BigDecimal bd = new BigDecimal(123456789); // 정수형 리터럴
BigDecimal bd = BigDecimal.valueOf(123.456789); // 실수형 리터럴
BigDecimal bd = BigDecimal.valueOf(123456789); // 정수형 리터럴

생성의 예제에서 실수형 타입의 값을 매개변수로 사용하면 오차가 발생한다.

BigDecimal bd = new BigDecimal(0.1); // 0.100000....5511...
BigDecimal bd = new BigDecimal("0.1"); // 0.1

BigDecimal의 연산

BigDecimal b = new BigDecimal("123");

// 더하기
BigDecimal b1 = b.add(BigDecimal.ONE); // 124
// 빼기
BigDecimal b2 = b.subtract(BigDecimal.TEN); // 113
// 나누기
BigDecimal b3 = b.divide(new BigDecimal("10")); // 12
// 나머지
BigDecimal b4 = b.remainder(new BigDecimal("10")); // 3;

BigDecimal의 반올림

BigDecimal b1 = new BigDecimal("12.34").setScale(0, RoundingMode.UP); // 13 UP
BigDecimal b2 = new BigDecimal("-12.34").setScale(0, RoundingMode.UP); // -13 UP

BigDecimal b3 = new BigDecimal("12.34").setScale(0, RoundingMode.DOWN); // 12 DOWN
BigDecimal b4 = new BigDecimal("-12.34").setScale(0, RoundingMode.DOWN); // -12 DOWN

BigDecimal b5 = new BigDecimal("12.34").setScale(0, RoundingMode.CEILING); // 13 CEILING
BigDecimal b6 = new BigDecimal("-12.34").setScale(0, RoundingMode.CEILING); // -12 CEILING

BigDecimal b7 = new BigDecimal("12.34").setScale(0, RoundingMode.FLOOR); // 12 FLOOR
BigDecimal b8 = new BigDecimal("-12.34").setScale(0, RoundingMode.FLOOR); // -13 FLOOR

BigDecimal b9 = new BigDecimal("12.44").setScale(0, RoundingMode.HALF_UP); // 12 HALF_UP
BigDecimal b10 = new BigDecimal("12.54").setScale(0, RoundingMode.HALF_UP); // 13 HALF_UP

BigDecimal b11 = new BigDecimal("12.50").setScale(0, RoundingMode.HALF_DOWN); // 12 HALF_DOWN
BigDecimal b12 = new BigDecimal("12.61").setScale(0, RoundingMode.HALF_DOWN); // 13 HALF_DOWN

BigDecimal b13 = new BigDecimal("12.50").setScale(0, RoundingMode.HALF_EVEN); // 12 HALF_EVEN
BigDecimal b14 = new BigDecimal("12.61").setScale(0, RoundingMode.HALF_EVEN); // 13 HALF_EVEN

BigDecimal b15 = new BigDecimal("12").setScale(0, RoundingMode.UNNECESSARY); // 12 UNNECESSARY
BigDecimal b16 = new BigDecimal("12.1").setScale(0, RoundingMode.UNNECESSARY); // ArithemeticException UNNECESSARY
  • setScale의 RoundingMode를 설정하지 않으면 기본으로 UNNECESSARY가 설정된다.
  • setScale의 첫번째 인자 값은 소수점 자릿수이다. 0번부터 시작하며 몇 번째 소수점을 올림 또는 내림할 것인지 지정하는 것이다.
  • setScale의 두번째 인자 값은 올림, 반올림, 내림, 반내림 등 어떤 모드로 설정할 것인지에 대한 값이다.

BigDecimal의 반올림 정리표

아래의 표는 RoundingMode enum을 참고하였다.

  UP DOWN CEILING FLOOR HALF_UP HALF_DOWN HALF_EVEN UNNECESSARY
5.5 6 5 6 5 6 5 6 throw ArithmeE..
2.5 3 2 3 2 3 2 2 throw ArithmeE..
1.6 2 1 2 1 2 2 2 throw ArithmeE..
1.1 2 1 2 1 1 1 1 throw ArithmeE..
1.0 1 1 1 1 1 1 1 1
-1.0 -1 -1 -1 -1 -1 -1 -1 -1
-1.1 -2 -1 -1 -2 -1 -1 -1 throw ArithmeE..
-1.6 -2 -1 -1 -2 -2 -2 -2 throw ArithmeE..
-2.5 -3 -2 -2 -3 -3 -2 -2 throw ArithmeE..
-5.5 -6 -5 -5 -6 -6 -5 -6 throw ArithmeE..
  • UP : 양수 -> 올림, 음수 -> 내림
  • DOWN : 양수 -> 내림, 음수 -> 올림
  • CEILING : 올림
  • FLOOR : 내림
  • HALF_UP : 5이상 올림, 5 미만 버림 (반올림)
  • HALF_DOWN : 6이상 올림, 6 미만 버림 (반올림)
  • HALF_EVEN : 자리의 값이 짝수 -> HALF_DOWN, 홀수 -> HALF_UP
  • UNNECESSARY : 나눗셈 결과가 딱 떨어지는 수가 아니면 throw ArithemeticException 발생

참고

* http://tcpschool.com/java/java_datatype_floatingPointNumber

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

* https://madplay.github.io/post/the-need-for-bigdecimal-in-java

 

자바 BigDecimal: 정확한 실수의 표현과 부동 소수점

자바에서 정확하게 실수를 표현하려면 어떻게 해야 할까? 그리고 부동 소수점 방식이란 무엇일까?

madplay.github.io

 

'개발 언어 > Java' 카테고리의 다른 글

[Java] 연산자  (0) 2021.08.18
[Java] 형변환  (0) 2021.08.18
[Java] 오버 플로우, 언더 플로우  (0) 2021.08.12
[Java] 상수와 리터럴  (0) 2021.08.11
[Java] 기본형과 참조형  (0) 2021.08.11

+ Recent posts