본문 바로가기
JAVA/기본 이론

5. 연산자(Operator)와 연산 순위

by D.O.T 2024. 2. 10.
연산자란 ?

 

우리는 연산자에 대해 이미 알고 있습니다. +, -, *, / 와 같은 연산 기호를 의미하는데요.

프로그래밍 언어에서는 사칙 연산 외에도 다양한 연산자가 있습니다.

 

사칙연산 + 나머지 연산
public class Main
{
	public static void main(String[] args) {
	    int a = 18, b = 4;
	    System.out.printf("a + b = %d\n", a + b);
	    System.out.printf("a - b = %d\n", a - b);
	    System.out.printf("a * b = %d\n", a * b);
	    System.out.printf("a / b = %d\n", a / b);
	    System.out.printf("a %% b = %d\n", a % b);
	}
}

컴퓨터에서는 사칙연산은 우리가 사용하는 연산자와 동일해서 쉽습니다.

추가로 modulo 연산은 % 기호를 이용해서 사용하는데요.

자바에서 printf와 같은 출력함수는 %d, %f 와 같이 출력형식 지정자인 %를 이용합니다.

%를 표현하기 위해서는 두번 작성하여 escape를 해주어야 하는데 자세한 내용은 입출력에서 다루도록 하겠습니다.

예상 결과는 22, 14, 72, 4, 2 입니다.

시프트 연산자
public class Main
{
	public static void main(String[] args) {
	    // 1010
	    int a = 10;
	    
	    // left shift, 10100 
	    a = a << 1;
	    System.out.printf("a = %d\n", a);
	    
	    // right shift, 1010
	    a = a >> 1;
	    System.out.printf("a = %d\n", a);
	    
	    // left shift twice, 101000
	    a = a << 2;
	    System.out.printf("a = %d\n", a);
	}
}

컴퓨터에서 숫자를 표현하기 위해서는 2진법으로 표현하게 됩니다.

10의 경우는 1010이 되죠.

shift 연산을 하게되면 bit를 왼쪽 또는 오른쪽으로 일정수만큼 밀어버리겠다는 의미가 됩니다.

 

10진수로 예를 들어 보겠습니다.

10을 100으로 표현하려면 어떻게 하나요? 10을 곱해주면 됩니다.

10을 1000으로 표현하려면 ? 10을 2번 곱해주면 됩니다.

특징은 0이 채워진만큼 10을 곱했다는 것이죠. (10진수 이므로)

 

2진수에서도 마찬가지입니다.

10을 20으로 표현하려면 2를 곱하면 됩니다.

이 때, 2진수는 2까지 나타내므로 2진수 상에서는 뒤에 0이 채워지게 됩니다.

1010에서 10100이 되는 것이죠.

 

이런 수학적 특성을 이용한 것이 shift 연산입니다.

곱하기 연산보다 빠릅니다.

 

비트 연산자
public class Main
{
	public static void main(String[] args) {
	    // 1010
	    int a = 10;
	    // 1111
	    int b = 15;
	    System.out.printf("a and b 연산 %d\n", a & b);          
	    System.out.printf("a or b 연산 %d\n", a | b);           
	    System.out.printf("a xor b 연산 %d\n", a ^ b);          
	    System.out.printf("not a 연산 %d\n", ~a);            
	    System.out.printf("not b 연산 %d\n", ~b);              
	    System.out.printf("notAnd(nand) 연산 %d\n", ~(a & b));
	    System.out.printf("notOr(nor) 연산 %d\n", ~(a | b));
	    System.out.printf("notXor 연산 %d\n", ~(a ^ b));
	}
}

코딩을 하다보면 속도의 문제로 2진수 간에 비트연산을 하게 되는 경우가 발생합니다.

이유는 일반적인 사칙연산보다 빠른 속도의 장점이 있기 때문입니다.

a는 10(10) → 1010(2), b는 15(10) → 1111(2) 로 변환 할 수 있습니다.

1010과 1111의 and, or, xor 연산은 각 각 1010, 1111, 0101로 10, 15, 5의 값을 가집니다.

다만 자바에서 not (~) 연산은 2의 보수 방법을 사용하는데요.

 

32비트 환경 자바에서 1010(2)인 경우 00000000 00000000 00000000 00001010으로 표현합니다.

이에 not 연산을 적용하면 11111111 11111111 11111111 11110101 이 되고 맨 상위 bit인 MSB가 1인 것을 확인합니다.

자바는 해당 숫자가 음수라고 인식하고 2의 보수를 취하게 됩니다.

11111111 11111111 11111111 11110101 에서 부호가 minus라고 인식 후 1의 보수,

00000000 00000000 00000000 00001010 에서 2의 보수,

00000000 00000000 00000000 00001011 이라는 결과 생성

해당 값은 11이라는 값을 가지고 부호는 minus이므로 -11(2)라는 결과를 도출하게 됩니다.

 

그 외 다른 값들도 한 번 직접 연산해보시는 것을 추천합니다.

 

대입 연산자
public class Main
{
	public static void main(String[] args) {
	    int a = 10;
	    a <<= 1;        // a = a << 1
	    System.out.printf("shift 대입 연산 : %d\n", a);
	    a *= 2;         // a = a * 2
	    System.out.printf("사칙 대입 연산 : %d\n", a);
	    a &= 10;        // a = a & 10
	    System.out.printf("비트 대입 연산 : %d\n", a);
	}
}

대입 연산의 경우, 위에서 다룬 사칙연산, shift연산, 비트 연산에만 적용이 되는 연산입니다.

변수 입장에서 자기 자신에게 특정 값을 연산한다면 대입 연산자를 사용할 수 있습니다.

주석에서 풀어놓은 문장과 같은 의미를 가집니다.

 

증감 연산자
public class Main
{
	public static void main(String[] args) {
	    int a = 0;
	    System.out.printf("a = %d\n", a++);
	    System.out.printf("a = %d\n", a);
	    System.out.printf("a = %d\n", ++a);
	    System.out.printf("a = %d\n", a--);
	    System.out.printf("a = %d\n", a);
	    System.out.printf("a = %d\n", ++a);
	}
}

증감 연산은 값을 1을 증가하거나 1을 감소하는 연산을 의미합니다.

증감 연산에는 전치 연산과 후치 연산이 있습니다.

전치 연산이란 변수의 데이터에 연산을 수행한 후 데이터를 전달하는 것입니다.

후치 연산이란 데이터를 전달하고 데이터에 연산을 수행하는 것입니다.

int a = 0;

// 전치 연산
a = a + 1;
System.out.printf("a = %d\n", a);

// 후치 연산
System.out.printf("a = %d\n", a);
a = a + 1

 

비교(관계) 연산자
public class Main
{
	public static void main(String[] args) {
	    System.out.printf("1 > 2 : %b\n", (1 > 2));
	    System.out.printf("1 >= 2 : %b\n", (1 >= 2));
	    System.out.printf("1 < 2 : %b\n", (1 < 2));
	    System.out.printf("1 <= 2 = %b\n", (1 <= 2));
	    System.out.printf("1 == 2 = %b\n", (1 == 2));
	    System.out.printf("2 >= 2 = %b\n", (2 >= 2));
	    System.out.printf("2 <= 2 = %b\n", (2 <= 2));
	    System.out.printf("2 == 2 = %b\n", (2 == 2));
	}
}

이것 또한 우리가 알고 있는 그대로 입니다.

대신 비교 연산자가 나타내는 값은 true or false로, 1 또는 0만 표현합니다.

 

논리 연산
public class Main
{
	public static void main(String[] args) {
	    System.out.printf("false and false : %b\n", false && false);
	    System.out.printf("false or false : %b\n", false || false);
	    System.out.printf("true and false : %b\n", true && false);
	    System.out.printf("true or false : %b\n", true || false);
	    System.out.printf("false and true : %b\n", false && true);
	    System.out.printf("false or true : %b\n", false || true);
	    System.out.printf("true and true : %b\n", true && true);
	    System.out.printf("true or true : %b\n", true || true);
	    System.out.printf("not true : %b\n", !true);
	    System.out.printf("not false : %b\n", !false);
	}
}

 

기호 때문에 비트 연산자와 헷갈리는 사람이 다수 있는 문제입니다.

비트 연산자는 10진수를 2진수 연산을 하기 때문에 숫자로 연산이 가능합니다.

하지만 자바에서 논리 연산은 boolean(true 또는 false)으로만 연산을 할 수 있습니다.

주로 조건이 만족하는 지 확인하기 위해서 사용합니다.

 

삼항 연산자
public class Main
{
	public static void main(String[] args) {
	    int a = 10, b = 20;
	    System.out.printf("bigger : %d\n", a > b ? a : b);
	    System.out.printf("%s\n", 10 < 2 ? "Yes" : "No");
	}
}

최대 값을 찾을 때 사용하거나 코드를 간결하게 하고 싶을 때, 사용하는 연산자입니다.

하지만, 실무에서는 잘 사용되지 않는 것으로 알고 있습니다.

우아한 형제들 자바 컨벤션에도 비추하고 있습니다.

 

수행과정은 아래와 같습니다.

1. '?' 를 기준으로 왼쪽의 연산을 먼저 수행합니다.

2. 결과 값이 true라면 ' : ' 을 기준으로 왼쪽 값을 전달

3. 결과 값이 false라면 ' : ' 을 기준으로 오른쪽 값을 전달합니다.

쉽게 생각하면 이 조건이 맞아 ? 응 : 아니 로 생각하시면 됩니다.

 

사용을 한다면 조건부인 ' ? ' 왼쪽은 괄호로 묶어 주시는게 좋습니다.

( a > b ) ? a : b


연산자의 우선순위??

 

수학에서 사칙 연산을 할 때, 곱하기와 나누기가 덧셈 뺄셈보다 먼저 연산하듯이 우선순위가 있다.

1. 괄호 : ' () '

2. 단항 연산자 : ' +, - , ++, --, !, ~'

- 변수또는 상수 자체에 붙어서 하나의 숫자로 표현하는 형태이다.

- (+1), (-1), (i++), (!true), ...

3. 산술 연산자 : ' +, -, /, *, % '

- 기본적인 사칙연산과 mod연산을 포함한다.

4. 시프트 연산자 : ' <<, >> '

5. 비교 연산자 : ' <, >, <=, >= '

6. 비교(등호, 동등) 연산자 : ' ==, != '

- a > b == b < a 인 경우, 문제 없다.

- a == b > (a < b) 인 경우, 문제가 발생한다. ( a == b ) > ( a < b )로 표현해야한다.

7. 비트 AND, XOR, OR 연산자 : ' &, ^, | '

- AND, XOR, OR 순서대로 연산자 우선순위가 존재한다.

- 그 이유는 명확하지 않다. 자바 설계자들이 그렇게 설계 했다.

- 추측하는 이유는 몇 가지 있으나 모르곘다.

8. 논리 AND, OR 연산자 : ' &&, || ' 

- 이 또한 순서대로 우선순위가 있다.

9. 삼항 연산자 : ' (조건) ? (true) : (false) '

10. 대입 연산자 : " =, +=, -= , ... '

- 모든 연산을 마친 후에 대입하기 때문에 당연하다.

 

되게 많은 순서가 있지만 생각보다 외우기 싶다.

가장 중요한 부분은 비교 연산과 논리 연산의 우선순위이다.

직접 실행하면서 테스트 해보는 것을 추천

 

노션 정리 URL

 

https://www.notion.so/24jihwan/b002389aea204cd2a1a15bd8ff2f358e

 

연산자 | Notion

산술, 대입, 연산자

24jihwan.notion.site

 

'JAVA > 기본 이론' 카테고리의 다른 글

7. 다차원 배열 (Multi Dimensional Array)  (1) 2024.02.10
6. 배열 (Array)  (1) 2024.02.10
3. 자료형(DataType)  (0) 2024.01.02
2. 변수(Variable)와 할당(Assign)  (1) 2024.01.02
1. 자바 시작하기  (1) 2024.01.02