Chapter 03 | 연산자

종이·2023년 2월 9일

이것이 자바다

목록 보기
2/6
post-thumbnail

3.1 부호/증감 연산자

부호 연산자 : 변수의 부호를 유지하거나 변경

연산식설명
+피연산자의 부호 유지
-피연산자의 부호 변경
byte b = 100;
byte result =  -b;   //컴파일 에러

정수 타입 연산의 결과는 int 타입이다.
부호를 변경하는 것도 연산이므로 다음과 같이 int타입 변수에 대입해야 한다.

byte b = 100;
int result = -b;

증감 연산자 : 변수의 값을 1증가 또는 감소시키는 연산자

연산식설명
++피연산자피연산자의 값을 1증가
--피연산자피연산자의 값을 1감소
피연산자++다른 연산 수행 후 피연산자의 값을 1증가
피연산자--다른 연산 수행 후 피연산자의 값을 1감소
int x = 1;
int y = 1;
int result1 = ++x + 10; // x값 1증가 -> int result1 = 2 + 10, x의 값 2
int result2 = y++ = 10 // int result2 = 1 + 10, y의 값 2

3.2 산술 연산자

산술 연산자는 더하기(+), 빼기(-), 곱하기(*), 나누기(/), 나머지(%) 총 5개이다.

//산술 연산자 예제
package Ch03;

public class ArithmeticOperatorExample {
    public static void main(String[] args) {
        byte v1 = 10;
        byte v2 = 4;
        int v3 = 5;
        long v4 = 10L;

        int result1  = v1 + v2;   //모든 피연산자는 int 타입으로 자동 변환 후 연산
        System.out.println("result1: " + result1);

        long result2 = v1 + v2 - v4; //모든 피연산자는 long 타입으로 자동 변환 후 연산
        System.out.println("result2: " + result2);

        double result3 = (double) v1 / v2;  //double 타입으로 강제 변환 후 연산
        System.out.println("result3: " + result3);

        int result4 = v1 % v2;
        System.out.println("result4: " + result4);
    }
}
실행 결과
result1: 14
result2: 4
result3: 2.5
result4: 2

3.3 오버플로우와 언더플로우

오버플로우(overflow) : 타입이 허용하는 최대값을 벗어나는 것
언더플로우(underflow) : 타입이 허용하는 최소값을 벗어나는 것

정수 타입 연산에서 오버플로우 혹은 언더플로우가 발생되면 해당 정수 타입의 최소값 또는 최대값으로 되돌아간다.
ex) byte타입 최대값 127에서 1을 더하면 오버플로우가 발생하여 최소값인 -128이 된다

// 오버플로우 언더플로우 예제
package Ch03;

public class OverflowUnderflowExample {
    public static void main(String[] args) {
        byte var1 = 125;
        for(int i = 0; i < 5; i++){
            var1++;
            System.out.println("var1: " + var1);
        }
        System.out.println("------------------------------------------");

        byte var2 = -125;
        for(int i = 0; i < 5; i++){
            var2--;
            System.out.println("var2: " + var2);
        }
    }
}
실행 결과
var1: 126
var1: 127
var1: -128
var1: -127
var1: -126
------------------------------------------
var2: -126
var2: -127
var2: -128
var2: 127
var2: 126

연산 과정 중에 발생하는 오버플로우와 언더플로우는 우리가 기대하는 값이 아니므로 항상 해당 타입 범위 내에서 연산이 수행되도록 코딩에 신경써야 한다.

3.4 정확한 계산은 정수 연산으로

산술 연산을 정확하게 계산하고 싶다면 실수 타입을 사용하지 않는 것이 좋다.

//다음 예제는 사과 1개를 0.1 단위의 10조각으로 보고 그 중 7조각을 뺀 3조각을 result에 저장한다.
package Ch03;

public class AccuracyExample1 {
    public static void main(String[] args) {
        int apple = 1;
        double pieceUnit = 0.1;
        int number = 7;

        double result = apple - number * pieceUnit;
        System.out.println("사과 1개에서 남은 양: " + result);
    }
}
실행 결과
사과 1개에서 남은 양: 0.29999999999999993

결과를 보면 변수의 값은 정확이 0.3이 되지 않는다.
이것은 부동 소수점 방식을 사용하는 실수 타입에서 흔히 일어나는데, 그렇기에 정확한 계산이 필요하다면 정수 연산으로 변경해 계산하는 것이 좋다.

package Ch03;

public class AccuracyExample2 {
    public static void main(String[] args) {
        int apple = 1;
        int totalPieces = apple * 10;
        int number = 7;

        int result = totalPieces - number;
        System.out.println("10조각에서 남은 조각: " + result);
        System.out.println("사과 1개에서 남은 양: " + result/10.0);
    }
}
실행 결과
10조각에서 남은 조각: 3
사과 1개에서 남은 양: 0.3

3.5 나눗셈 연산 후 NaN과 Infinity 처리

나눗셈(/) 또는 나머지(%) 연산에서 좌측 피연산자가 정수, 우측 피연산자가 0일 경우 무한대의 값을 정수로 표현할 수 없기 때문에 예외가 발생.

좌측 피연산자가 실수이거나 우측 피연산자가 0.0 또는 0.0f이면 연산의 결과는 Infinity(무한대) 또는 NaN(Not a Number)이 된다.
5 / 0.0 -> Infinity
5 % 0.0 -> NaN

Infinity 또는 NaN 상태에서 연산을 계속하면 계속해서 Infinity와 NaN이 되어 데이터가 엉망이 될 수 있다.

이를 방지하기 위해 Double.isInfinite()와 Double.isNaN()을 사용해 Infinity 또는 NaN인지 확인 가능하다.

package Ch03;

public class InfinityAndAnACheckExample {
    public static void main(String[] args) {
        int x = 5;
        double y = 0.0;
        double z = x / y;
        //double z = x % y;

        if(Double.isInfinite(z) || Double.isNaN(z)){
            System.out.println("값 산출 불가");
        }else{
            System.out.println(z + 2);
        }
    }
}
실행 결과
값 산출 불가

3.6 비교 연산자

비교 연산자는 동등 또는 크기를 평가해서 boolean 타입인 true/false를 산출한다.

구분연산식설명
동등 비교==두 피연산자의 값이 같은지 검사
동등 비교!=두 피연산자의 값이 다른지 검사
크기 비교>, <큰지, 작은지 검사
크기 비교>=, =<크거나 같은지, 작거나 같은지 검사

피연산자의 타입이 다를 경우에는 비교 연산을 수행하기 전에 타입을 일치시킨다.
ex) 'A' == 65는 'A'가 int 타입으로 변환되어 65가 된 다음 65 == 65로 비교한다.

문자열 비교는 동등 연산자 대신 equals()와 !equals()를 사용한다.

boolean result = str1.equals(str2) // 문자열이 같은지 검사(대소문자 구분)
boolean result = !str1.equals(str2) // 문자열이 다른지 검사
//예제
package Ch03;

public class CompareOperatorExample {
    public static void main(String[] args) {

        int num1 = 10;
        int num2 = 10;
        boolean result1 = (num1 == num2);
        boolean result2 = (num1 != num2);
        boolean result3 = (num1 <= num2);
        System.out.println("result1: " + result1);
        System.out.println("result2: " + result2);
        System.out.println("result3: " + result3);

        char char1 = 'A';
        char char2 = 'B';
        boolean result4 = (char1 < char2); //65 < 66
        System.out.println("result4: " + result4);

        int num3 = 1;
        double num4 = 1.0;
        boolean result5 = (num3 == num4);
        System.out.println("result5: " + result5);

        float num5 = 0.1f;
        double num6 = 0.1;
        boolean result6 = (num5 == num6);
        boolean result7 = (num5 == (float)num6);
        System.out.println("result6: " + result6);
        System.out.println("result7: " + result7);

        String str1 = "자바";
        String str2 = "Java";
        boolean result8 = (str1.equals(str2));
        boolean result9 = (!str1.equals(str2));
        System.out.println("result8: " + result8);
        System.out.println("result9: " + result9);
    }
}
실행 결과
result1: true
result2: false
result3: true
result4: true
result5: true
result6: false
result7: true
result8: false
result9: true

3.7 논리 연산자

논리 연산자는 논리곱(&&), 논리합(||), 배타적 논리합(^) 그리고 논리 부정(!) 연산을 수행한다.

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

&&와 &는 산출 결과는 같지만 연산 과정이 조금 다르다.
&&는 앞의 피연산자가 false면 뒤의 피연산자를 평가하지 않고 바로 false를 산출하지만,
&는 두 피연산자 모두를 평가해서 산출 결과를 낸다.
|| 와 |도 마찬가지이다.

// 예제
package Ch03;

public class LogicalOperatorExample {
    public static void main(String[] args) {
        int charCode = 'A';

        if((65 <= charCode) & (charCode <= 90)){
            System.out.println("대문자군요");
        }

        if((97 <= charCode) & (charCode <= 122)){
            System.out.println("소문자군요");
        }

        if((48 <= charCode) & (charCode <= 57)){
            System.out.println("0~9 숫자군요");
        }

        int value = 6;

        if((value % 2 == 0) | (value % 3 == 0)){
            System.out.println("2 또는 3의 배수군요");
        }

        boolean result = (value % 2 == 0) | (value % 3 == 0);
        if(!result){
            System.out.println("2 또는 3의 배수가 아니군요");
        }
    }
}
실행 결과
대문자군요
2 또는 3의 배수군요

3.8 비트 논리 연산자

비트 논리 연산자는 bit 단위로 논리 연산을 수행한다.
0과 1이 피연산자가 되므로 2진수 0과 1로 저장되는 정수 타입만 피연산자가 될 수 있다.
산출 결과가 1(true), 0(false)로 나오는걸 제외하면 논리 연산자와 차이가 없다.

비트 논리 연산자는 byte, short, char 타입 피연산자를 int 타입으로 자동 변환한 후 연산을 수행하기 때문에 int 변수에 대입해야 한다.

3.9 비트 이동 연산자

비트 이동 연산자는 좌측 또는 우측으로 밀어서 이동시키는 연산을 수행한다

연산식설명
a << b정수 a의 각 비트를 b만큼 왼쪽으로 이동, 오른쪽 빈자리는 0으로 채움, a * 2^b와 동일한 결과
a >> b정수 a의 각 비트를 b만큼 오른쪽으로 이동, 왼쪽 빈자리는 최상위 부호와 같은 값으로 채움, a / 2^b와 동일한 결과
a >>> b정수 a의 각 비트를 b만큼 오른쪽으로 이동, 왼쪽 빈자리는 0으로 채움
int result = 1 << 3;  
//4byte(32bit) 전체를 왼쪽으로 3비트 이동하면 맨 왼쪽 3비트는 밀려서 버려지고,
// 맨 오른쪽에 새로 생기는 3비트는 0으로 채워진다. 따라서 result에는 8이 저장된다.

1 << 3 = 1 * 2^3 = 8

//예제
package Ch03;

public class BitShiftExample {
    public static void main(String[] args) {
        int num1 = 1;
        int result1 = num1 << 3;
        int result2 = num1 * (int) Math.pow(2,3); //Math.pow는 double 값을 산출하기에 (int)로 캐스팅
        System.out.println("result1: " + result1);
        System.out.println("result2: " + result2);

        int num2 = -8;
        int result3 = num2 >> 3;
        int result4 = num2 / (int) Math.pow(2,3);
        System.out.println("result3: " + result3);
        System.out.println("result4: " + result4);
    }
}
실행 결과
result1: 8
result2: 8
result3: -1
result4: -1

이번엔 우측 이동 연산자(>>>)를 사용하여 정수-8을 3비트만큼 오른쪽으로 이동시켜보자

int result = -8 >>> 3;

//32비트 전체를 오른쪽으로 밀어 3비트는 밀려서 버려지고 맨 왼쪽에 새로 생긴 3비트는 0으로 채워진다.
//이렇게 변환된 2진수를 10진수로 변환하면 536870911값을 얻는다.

3.10 대입 연산자

대입 연산자는 우측 피연산자의 값을 좌측 피연산자인 변수에 대입한다.

//예제
package Ch03;

public class AssignmentOperatorExample {
    public static void main(String[] args) {
        int result = 0;
        result += 10;
        System.out.println("result= " + result);
        result -= 5;
        System.out.println("result= " + result);
        result *= 3;
        System.out.println("result= " + result);
        result /= 5;
        System.out.println("result= " + result);
        result %= 3;
        System.out.println("result= " + result);
    }
}
실행 결과
result= 10
result= 5
result= 15
result= 3
result= 0

3.11 삼항(조건) 연산자

삼항 연산자(피연산자 ? 피연산자 : 피연산자)는 총 3개의 피연산자를 가진다.
? 앞의 피연산자는 boolean 변수 또는 조건식이 오므로 조건 연산자라고도 한다.
이 값이 true면 콜론(:) 앞의 피연산자가 선택되고, false면 뒤의 피연산자가 선택된다.

//예제
package Ch03;

public class ConditionalOperationExample {
    public static void main(String[] args) {
        int score = 85;
        char grade = (score > 90) ? 'A' : ((score) > 80 ? 'B' : 'C');
        System.out.println(score + "점은 " + grade + "등급입니다");
    }
}
실행 결과
85점은 B등급입니다

3.12 연산의 방향과 우선순위

여러 가지 연산자들이 섞여 있다면 어느 연산자가 먼저 처리될지 혼란스러울 수 있으니
괄호()로 묶어서 표현하면 가독성이 좋아질 수 있다.

확인문제

  1. 다음 코드를 실행했을 때 출력 결과를 작성해보세요.
int x = 10;
int y = 20;
int z = (++x) + (y--); // x는 1을 증가해서 11, y는 연산 후 증가이기에 그대로 20
System.out.println(z);

31

  1. 다음 코드를 실행했을 때 출력 결과를 작성해보세요.
int score =  85;
String result = (!(score>90))? "가" : "나";
System.out.println(result);

  1. 534자루의 연필을 30명의 학생들에게 똑같은 개수로 나누어 줄 때 1인당 몇 개를 가질 수 있고, 마지막에 몇 개가 남는지를 구하는 코드입니다. ()에 들어갈 알맞은 코드를 차례대로 작성해보세요.
int pencils = 534;
int students = 30;
//학생 한 명이 가지는 연필 수
int pencilsPerStudent = (1);
System.out.println(pencilsPerStudent);
//남은 연필 수
int pencilsLeft = (2);
System.out.println(pencilsLeft);
  1. (pencils / students)
  2. (pencils % students)

4 다음은 십의 자리 이하를 버리는 코드입니다. 변수 value의 값이 356이라면 300이 나올 수 있도록 ()에 알맞은 코드를 작성하세요(산술 연산자만 사용)

int value = 356;
System.out.println( );

value - (value % 100)

  1. 다음 코드는 사다리꼴의 넓이를 구하는 코드입니다. 정확히 소수 자릿수가 나올 수 있도록 ()에 들어갈 수 있는 코드를 모두 선택하세요
int lengthTop = 5;
int lengthBottom = 10;
int height = 7;
double area = ( );
System.out.println(area);
  1. (lengthTop + lengthBottom) * height / 2.0
  2. (lengthTop + lengthBottom) height 1.0 / 2
  3. (double) (lengthTop + lengthBottom) * height / 2
  4. (double) ((lengthTop + lengthBottom) * height / 2)
  1. 다음 코드는 비교 연산자와 논리 연산자의 복합 연산식입니다. 연산식의 출력 결과를 작성해보세요.
int x = 10;
int y = 5;
System.out.println(x > 7) && (y <= 5));
System.out.println(x % 3 == 2) || (y % 2 != 1));

true
false

  1. 다음은 % 연산을 수행한 결과값에 10을 더하는 코드입니다. NaN 값을 검사해서 올바른 결과가 출력될 수 있도록 ()에 들어갈 코드를 작성해보세요.
double x = 5.0;
double y = 0.0;
double z = 5 % y;
if(){
	System.out.println("0.0으로 나눌 수 없습니다.");
    } else{
    	double result = z + 10;
        System.out.println("결과: " = result);
        }

Double.isNaN(z)

0개의 댓글