[혼공자] 03-2. 연산자의 종류

Benjamin·2023년 2월 25일
0

혼공자

목록 보기
9/27

03-2 연산자의 종류

피 연산자 수에 따라 구분하는 연산자를 기준으로 각각의 연산 방법을 알아보자.

단항 연산자

피연산자가 단 하나뿐인 연산자

  • 부호 연산자(+,-)
  • 증감 연산자(++, --)
  • 논리 부정 연산자

부호 연산자(+,-)

양수 및 음수를 표시하는 것 ( 변수의 부호를 유지하거나 변경한다)
boolean, char 타입 제외한 나머지 기본 타입에 사용할 수 있다.

  • + = 피연산자의 부호 유지
  • - = 피연산자의 부호 변경

정수 및 실수 타입 변수 앞에도 붙일 수 있다.

주의할 점은 부호 연산자의 결과가 int타입인것이다!

예 ) byte타입 변수를 부호 연산하면 int타입으로 변환된다.
따라서 다음 코드는 컴파일에러가 발생한다.

 byte b = 100;
 byte result = -b; // 컴파일 에러 발생 

부호연산자의 결과는 int타입이므로 int타입 변수에 저장해야한다.

 byte b = 100;
int result = -b; //-100

증감 연산자(++, --)

변수의 값을 1 증가(++)시키거나 1 감소(--)시키는 연산

boolean타입을 제외한 모든 기본타입의 피연산자에 사용 가능

연산자연산자설명
++피연산자다른 연산을 수행하기 전에 피연산자의 값을 1 증가시킴
--피연산자다른 연산을 수행하기 전에 피연산자의 값을 1 감소시킴
피연산자++다른 연산을 수행한 후에 피연산자의 값을 1 증가시킴
피연산자--다른 연산을 수행한 후에 피연산자의 값을 1 감소시킴

++연산자는 피연산자의 값에 1을 더해서 그 결과를 다시 피연산자에 저장한다.
따라서 이를 증가연산자라고 부른다.

-- 연산자는 피연산자의 값에서 1을 뺀 후 그 결과를 다시 피연산자에 저장한다.
따라서 이를 감소 연산자라고 부른다.

증가 연산자와 감소 연산자는 변수의 앞뒤 어디에든 올 수 있다.
연산식에서 증감연산자만 사용된다면 증감연산자의 위치는 상관없다.

++i;
i++;
//모두 i=i+1로 동일
--i;
i--;
//모두 i=i-1로 동일

하지만 다른 연산자와 함께 사용되면 증감 연산자의 위치에따라 연산식의 결과가 다르게 나오므로 주의해야한다!
증감 연산자가 변수앞에있으면 우선 변수값을 증가 혹은 감소시킨 후 다른 연산을하며, 증감 연산자가 변수 뒤에 있으면 다른 연산자를 먼저 처리한 후 변수 값을 증가 혹은 감소시킨다.

int x= 1;
int y=1;
int result1 = ++x + 10; //12
int result2 = y++ + 10; //11  
  • ++i 와 i = i+1의 연산속도
    많이들 전자가 후자보다 빠르다고 알고있다. 그 이유는 후자는 =연산자, + 연산자가 있기때문에 두 번의 연산이 필요하지만 전자는 ++ 한번의 연산만 수행하기때문이다.
    하지만! 둘 다 컴파일해서 생성된 바이트코드를 비교해보면 코드가 동일한것을 볼 수있다.
    따라서 둘의 연산속도 차이는 없다.

위 연산속도의 이야기를 확인해보기 위해 x = x +1코드를 살펴봤다.

public class HelloApplication {
    public static void main(String[] args) {
        int x = 1;
        x = x+1;
    }
}

위 코드로 짜고 컴파일한 후 바이트코드를 살펴보면 아래와같은 코드를 볼 수 있다.

다음으로 ++x를 이용한 코드를 살펴보자.

public class HelloApplication {
    public static void main(String[] args) {
        int x = 1;
        ++x;
    }
}

논리 부정 연산자(!)

true를 false로 false를 true로 변경하기 때문에 boolean타입에만 사용할 수 있다.

  • !피연산자 = 피연산자가 true면 false로, false면 true로 값을 산출

이는 조건문과 제어문에서 조건식의 값을 부정하도록해서 실행 흐름을 제어할 때 주로 사용한다.
두 가지 상태를 번갈아가며 변경하는 토글 기능을 구현할 때도 사용한다.

이항 연산자

피연산자가 2개인 연산자를 말한다.

  • 산술 연산자(+,-,*,/,%)
  • 문자열 결합 연산자(+)
  • 비교 연산자(<,>,<=,>=,==,!=)
  • 논리 연산자(&&,||,&,|,^,!)
  • 대입 연산자(+=,-=,*=,/=.%=)
    등등

산술 연산자(+,-,*,/,%)

사칙연산자인 덧셈, 뺄셈, 곱셈, 나눗셈, 나머지를 구하는 연산자까지 총 5개다.
이는 boolean타입을 제외한 모든 기본타입에 사용할 수 있다.

% 는 나눗셈을 수행한 후 몫이 아닌 나머지를 돌려주는 연산자이다.
예를들어 num%3에서 나올 수 있는 결과는 0,1,2중 하나의 값이된다.
왜냐하면 어떤 수를 3으로 나누었을 경우 나머지는 세 숫자 중 하나이기 때문이다.

산술연산자의 특징은 피연산자들의 타입이 동일하지 않을 경우 다음과 같은 규칙을 사용해서 피연산자의 타입을 일치시킨 후 연산을 수행한다는 점이다.

  • 피연산자들이 byte, short, char타입일 경우 모두 int타입으로 변환된 후 연산을 수행한다.
    예 ) byte + byte -> int + int = int
  • 피연산자들이 모두 정수타입이고 long 타입이 포함되어 있을 경우, 모두 long타입으로 변환된 후 연산을 수행한다.
    예) int + long -> long + long = long
  • 피연산자 중 실수 타입(float, double)이 있을 경우 허용 범위가 큰 실수타입으로 변환된 후 연산을 수행한다.
    예) int + double -> double + double = double

위 규칙에따라 아래 코드는 컴파일에러가 발생한다.

byte byte1=1;
byte byte2=1;
byte result = byte1 + byte2; //컴파일에러발생 

byte타입은 int타입으로 변환된 후 연산을 수행하기때문에 아래처럼 코드를 수정해야한다.

byte byte1=1;
byte byte2=1;
int result = byte1 + byte2; 

다음 코드는 어떨지 살펴보자.

int int1 = 10;
int int2 = 4;
int result = int1/int2; //2
double result2 = int1/int2; //2.0

result2의 결과를 보자.
int타입의 수를 연산한 후 결과가 2이므로,2를 실수화해서 2.0이 저장된다.

만약 수학적으로 2.5를 얻고싶다면 피연산자 int1,int2중 하나를 double타입으로 강제 타입 변환한 후 산술연산을 수행하면된다.

double result3 = (int1*1.0) / int2;
double result4 = (double)int1 / int2;
double result4 = int1 / (double)int2;

char도 정수타입이기때문에 산술 연산이 가능하다.
주의할점은 int타입으로 변환되므로 연산결과는 int타입이 된다는것이다.
따라서 다음과같은 예제는 컴파일에러가 발생한다

char c1 = 'A'+1; // char c1 = 66 으로 컴파일됨 
char c2 = 'A';
// char c3 = c2 + 1; // char변수가 산술 연산에 사용되면 int타입으로 변환되므로 연산 결과는 int타입이 됨

'A'+1은 리터럴 문자 'A'에 1을 더한것인데, 문자 'A'는 65라는 유니코드를 가지므로 이는 66이된다.
리터럴 간의 연산은 컴파일 단계에서 수행하기때문에 타입 변환이 없다.
(미리 연산해서 결과를 만들고, 결과 변수에 이를 저장하도록 바이트 코드를 생성)
따라서 c1은 아무 문제가 없다.

그러나 c3처럼 c2와 1을 더하면 c2는 int타입으로 변환되고 1과 연산되기 때문에 연산 결과는 int타입에 저장되어야하므로 컴파일 에러가 발생한다.
char c3 = (char)(c2+1)과 같이 강제 타입 변환을 해서 결과를 얻을 수 있다.

문자열 결합 연산자(+)

문자열을 서로 결합하는 연산자
+는 산술연산자, 부호 연산자인 동시에 문자열 결합연산자이기도 하다.
피연산자 중 한쪽이 문자열이면 + 연산자는 문자열 결합 연산자로 사용되어 다른 피연산자를 문자열로 변환하고 서로 결합한다.

String str1 = "JDK" + 6.0; //JDK6.0 
String str2 = str1 + "특징"; //JDK6.0특징

간혹 + 연산자가 산술 연산자인지 문자열 결합 연산자인지 판단하기 어려운 경우가 있다.

"JDK" + 3 + 3.0;

위 코드는 결과가 뭘까?
문자열과 숫자가 혼합된 + 연산식은 왼쪽에서부터 오른쪽을 연산이 진행된다.
따라서 "JDK"+3이 먼저 연산되어 "JDK3"이라는 문자열이 되고, 이것을 다시 3.0과 연산해 "JDK33.0"이라는 문자열 결과가 나온다.

하지만 다음의 경우는?

3 + 3.0 + "JDK";

3 + 3.0이 먼저 연산되어 6.0이라는 실수값이 되고, 이것을 "JDK"와 연산하여 '6.0JDK"라는 결과가 나온다.

이것처럼 어떤것이 먼저 연산되느냐에따라 다른 결과가 나오므로 주의할 필요가 있다.

비교 연산자(<,>,<=,>=,==,!=)

피연산자의 대소 또는 동등(==,!=)을 비교해서 true, false를 산출한다.
대소 연산자는 boolean을 제외한 기본 타입에 사용할 수 있고, 동등 연산자는 모든 타입에 사용할 수 있다.

비교연산자는 흐름 제어문인 조건문, 반복문에서 주로 이용되어 실행 흐름을 제어할 때 사용된다.

  • 동등 비교 : ==, !=
    == : 두 피연산자의 값이 같은지 검사
    != : 두 피연산자의 값이 다른지 검사
  • 크기 비교 : >,<,<=,>=
    A>B : A가 큰지 검사
    A<B : A가 작은지 검사
    A>=B : A가 크거나 같은지 검사
    A<=B : A가 작거나 같은지 검사

만약 피연산자가 char타입이면 유니코드값으로 비교연산을 수행한다.
'A' < 'B' -> 65 < 66

비교 연산자에서도 연산을 수행하기 전에 피연산자의 타입을 일치시킨다.
예를 들어 'A'==65 는 A가 int타입으로 변환되어 65 == 65로 비교한다.
'A' == 65 //true
마찬가지로 3 == 3.0에서 3이 double타입으로 변환되어 3.0 == 3.0으로 비교한다.
3 == 3.0 // true

그러나 한 가지 예외가 있는데 0.1 == 0.1f 같은 경우다.
정상적이라면 0.1f가 왼쪽 피연산자와 같은 double타입으로 변환되어 0.1 == 0.1이 되고 true가 산출되어야 하지만 이 결과값은 false가 된다.
0.1 == 0.1f // false

그 이유는 실수의 저장방식인 부동소수점 방식이 0.1을 정확히 표현할 수 없기 때문이다.
0.1f는 0.1의 근사값 0.10000000149011612로 표현된다.
따라서 0.1보다 큰 값이 되어 버린다. (0.2 == 0.2f 0.3 == 0.3f ... 도 false로 동일)

또한 Double과 Float가 각각 표현할 수 있는 유효자리수가 차이가 나기 때문에 같은 값을 가질 수 없다.
0.1f == 0.100000001490116119384765625
0.1d == 0.1000000000000000055511151231257827021181583404541015625

1.0 == 1.0f는 true (소수점아래에 자연수가 하나라도 오면 false인것으로 보임)

이것의 해결책은 피연산자를 모두 float타입으로 변환해서 비교하거나 정수 타입으로 변환해서 비교하는것이다.

  • 부동소수점
    2진수로 표현 못하는 소수 발생 (1/3을 소수로 표현하면 0.3333...이 되는 것과 비슷한 개념)
    어쩔 수 없이 컴퓨터에는 가장 근사치값이 저장됨
    이 근사값 저장 방법 = 고정 소수점(정수 bit, 소수 bit 정해두고 해당 bit만큼 사용), 부동 소수점
  • String 변수 비교
    String 문자열을 비교할 때에는 대소 비교 연산자(<,<=,>,>=)를 사용할 수 없고, 동등 비교연산자(==, !=)는 사용할 수 있다.
    하지만 이게 문자열이 같은지 다른지를 비교하는 용도로는 사용되지 않는다.
    기본타입의 변수값을 비교할때에는 동등연산자를 사용하지만, 참조 타입인 String변수를 비교할때는 equals() 메소드를 사용한다.

논리 연산자(&&,||,&,|,^,!)

논리곱(&&), 논리합(||), 배타적 논리합(^), 논리 부정(!) 연산을 수행한다.
피연산자는 boolean 타입만 가능하다.

구분연산식연산식연산식결과설명
AND(논리곱)true&& 또는 &truetrue피연산자가 모두 true일 경우에만 연산 결과가 true
AND(논리곱)true&& 또는 &falsefalse
AND(논리곱)false&& 또는 &truefalse
AND(논리곱)false&& 또는 &falsefalse
OR(논리합)true||또는 |truetrue피연산자중 하나만 true이면 연산 결과는 true
OR(논리합)true||또는 |falsetrue
OR(논리합)false||또는 |truetrue
OR(논리합)false||또는 |falsefalse
XOR(배타적 논리합)true^truefalse피연산자가 하나는 true이고 다른 하나가 false일 경우에만 연산 결과가 true
XOR(배타적 논리합)true^falsetrue
XOR(배타적 논리합)false^truetrue
XOR(배타적 논리합)false^falsefalse
NOT(논리 부정)!truefalse피연산자의 논리값을 바꿈
NOT(논리 부정)!falsetrue피연산자의 논리값을 바꿈

&&와 &는 산출 결과는 같지만 연산과정이 조금 다르다.

  • && : 앞의 피연산자가 false라면 뒤의 피연산자를 평사하지 않고 바로 false 결과 나타냄
    -> 하나라도 false이면 전체 연산식이 false이기때문
  • & : 두 피연산자 모두를 평가해서 산출결과를 낸다.

따라서 &보다 &&가 더 효율적으로 동작한다.

|| 와 |도 마찬가지이다.

  • || : 앞의 피연산자가 true이면 뒤의 피연산자를 평가하지않고 바로 true산출결과 나타냄
    -> 하나라도 true이면 결과가 true이기때문
  • | : 두 피연산자 모두를 평가해서 산출결과를 나타냄

따라서 ||가 |보다 더 효율적으로 동작한다.

논리연산은 흐름 제어문인 조건문, 반복문 등에서 주로 사용한다.

대입 연산자(+=,-=,*=,/=.%=)

오른쪽 피연산자의 값을 왼쪽 피연산자인 변수에 저장한다.
오른쪽 피연산자에는 리터럴 및 변수, 다른 연산식이 올 수 있다.

  • 단순 대입 연산자 = 단순히 오른쪽 피연산자의 값을 변수에 저장하는 것
  • 복합 대입 연산자 = 정해진 연산을 수행한 후 결과를 변수에 저장하는 것
구분연산식연산식연산식설명
단순 대입 연산자변수=피연산자오른쪽의 피연산자의 값을 왼쪽 변수에 저장
복합 대입 연산자변수+=피연산자변수 = 변수 + 피연산자와 동일
복합 대입 연산자변수-=피연산자변수 = 변수 - 피연산자와 동일
복합 대입 연산자변수*=피연산자변수 = 변수 * 피연산자와 동일
복합 대입 연산자변수/=피연산자변수 = 변수 / 피연산자와 동일
복합 대입 연산자변수%=피연산자변수 = 변수 % 피연산자와 동일
복합 대입 연산자변수&=피연산자변수 = 변수 & 피연산자와 동일
복합 대입 연산자변수|=피연산자변수 = 변수 | 피연산자와 동일
복합 대입 연산자변수^=피연산자변수 = 변수 ^ 피연산자와 동일

대입연산자는 모든 연산자들 중 가장 낮은 우선순위를 갖고있기떄문에 제일 마지막에 수행된다.
그리고 연산의 진행 방향이 오른쪽에서 왼쪽이다.

삼항 연산자

삼항연산자(?:)는 3개의 피연산자를 필요로하는 연산자이다.
?앞의 조건식에 따라 콜론(:) 앞뒤의 피연산자가 선택된다고해서 조건 연산식이라고 부르기도한다.

조건식(피연산자) ? true일때 값 또는 연산식(피연산자2) : false일때 값 또는 연산식(피연산자3)

피연산자2와 피연산자3에는 주로 값이 오지만, 연산식이 올수도있다!

삼항연산자는 if문으로 변경해서 작성할수도있지만, 한줄에 간단하게 작성하려면 삼항 연산자를 이용하는것이 더 효율적이다.

int score = 85;
char grade = (score > 90) ? 'A' : ((score>80) ? 'B' : 'C'); //B의 결과를 가짐

십의 자리 이하를 버리는 코드

int value = 365;
System.out.println(value/100*100);

다음 코드의 실행결과가 "10%가 아닙니다."인 이유?

float var1 = 10f;
float var2 = var1/100;
if(var2 == 0.1) System.out.println("10% 입니다.");
else System.out.println("10%가 아닙니다.");

float형의 0.1은 정확히 0.1이 아니기떄문이다.


출처
혼자공부하는 자바

0개의 댓글