나를 기록하다
article thumbnail
반응형

5. 논리 연산자


  • ||(OR결합) 피연사자 중 어느 한 쪽만 true이면 true를 결과로 얻는다.
  • &&(AND결합) 피연산자 양쪽 모두 true이어야 true를 결과로 얻는다.
  • [예제] 사용자로부터 문자를 입력받아서 숫자, 영문자(소문자), 영문자(대문자) 구별하기
public class _16_LogicalOperator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        char ch = ' ';

        System.out.print("문자를 하나 입력하세요 : ");

        String input = scanner.nextLine();
        ch = input.charAt(0);

        if ('0' <= ch && ch <= '9') {
            System.out.printf("숫자입니다.%n");
        } else if ('A' <= ch && ch <= 'Z') {
            System.out.printf("영문자(대문자)입니다.%n");
        } else if ('a' <= ch && ch <= 'z') {
            System.out.printf("영문자(소문자)입니다.%n");
        } else {
            System.out.println("잘못된 값을 입력하셨습니다. 프로그램을 종료합니다.");
        }
    }
}
// 결과
문자를 하나 입력하세요 : 3
숫자입니다.

문자를 하나 입력하세요 : a
영문자(소문자)입니다.

문자를 하나 입력하세요 : F
영문자(대문자)입니다.

문자를 하나 입력하세요 : ㄱ
잘못된 값을 입력하셨습니다. 프로그램을 종료합니다.

 

 

5.1 효율적인 연산

  • 같은 조건식이라도 피연산자의 위치에 따라 연산속도가 달라질 수 있다.
    OR 연산의 경우 연산결과가 ‘참’일 확률이 높은 피연산자를 왼쪽!
  • ex) 대소문자 확인 시 사용자가 소문자 입력을 할 확률이 높을 경우
    (’a’ <= ch && ch <= ‘z’) || (’A’ <= ch && ch <= ‘Z’)

 

5.2 비트 연산자

5.2.1 & | ^ ~ << >>

  • |(OR) : 피연산자 중 한 쪽의 값이 1이면 1. 그 외에 0
  • &(AND) : 피연산자 양 쪽이 모두 1이어야만 1. 그 외에 0
  • ^(XOR) : 피연산자의 값이 서로 다를 때만 1. 같을 때 0, ^ : 배타적 XOR(eXclusive OR)이라 부름
  • [예제] 0xAB의 마지막 4 bit를 ‘F’로 변경하는 방법 ‘|’ : 0xAB | 0xF = ?
0xAB : 1 0 1 0 1 0 1 1

0xF  : 0 0 0 0 1 1 1 1

     |) ——————————————

       1 0 1 0 1 1 1 1 → 0xAF

 

  • [예제] 특정 비트의 값을 뽑아낼 때 사용하는 ‘&’ : 0xAB & 0xF = ?
0xAB  : 1 0 1 0 1 0 1 1

0xF   : 0 0 0 0 1 1 1 1

    &)  ——————————————

       0 0 0 0 1 0 1 1 → 0xB
  • [예제] 같은 값으로 두고 XOR 연산을 시행하면 원래의 값으로 돌아오는 특징 → 간단한 암호화에 사용 ‘^’ : 0xAB ^ 0xF = ?
0xAB  : 1 0 1 0 1 0 1 1

0xF   : 0 0 0 0 1 1 1 1

    ^)  ——————————————

        1 0 1 0 0 1 0 0 → 0xA4
0xA4  : 1 0 1 0 0 1 0 0

0xF   : 0 0 0 0 1 1 1 1

    ^)  ——————————————

        1 0 1 0 1 0 1 1 → 0xAB

→ 편의상 2진수 8자리로 표현했지만 int 타입(4 byte)간의 연산이라 32자리로 표현하는 것이 맞다.

또한 비트 연산에서도 피연산자의 타입을 일치시키는 ‘산술 변환’이 일어날 수 있음.

 

5.2.2 비트 전환 연산자 ~

  • 이 연산자는 피연산자를 2진수로 표현했을 때, 0은 1로, 1은 0으로 바꾼다. 논리부정 연산자 ‘!’와 유사하다.
  • ‘~’에 의해 ‘비트 전환’이 되고 나면 피연산자의 ‘1의 보수’를 얻을 수 있음 → ‘1의 보수’ 연산자라고도 부른다.
  • [예제] 10진수를 2진수로 변환하는 toBinaryString()을 이용하여 비트 전환 연산자 출력
public class _17_BitSwitchingOperator {
    public static void main(String[] args) {
        byte p = 10;
        byte n = -10;

        System.out.printf(" p   =%d \\t%s%n",    p,      toBinaryString(p));
        System.out.printf(" p   =%d \\t%s%n",    ~p,     toBinaryString(~p));
        System.out.printf(" p   =%d \\t%s%n",    ~p+1,   toBinaryString(~p+1));
        System.out.printf(" p   =%d \\t%s%n",    ~~p,    toBinaryString(~~p));
        System.out.println();
        System.out.printf(" n   =%d%n", n);
        System.out.printf("~(n-1) =%d%n", ~(n-1));
    }
//    10진 정수를 2진수로 변환하는 메서드
    static String toBinaryString(int x) {
        String zero = "00000000000000000000000000000000";
        String tmp = zero + Integer.toBinaryString(x);
        return tmp.substring(tmp.length()-32);
    }
}
// 결과
p   =10 	00000000000000000000000000001010
p   =-11 	11111111111111111111111111110101
p   =-10 	11111111111111111111111111110110
p   =10 	00000000000000000000000000001010

  n    =-10
~(n-1) = 10

5.2.3 쉬프트 연산자 << >>

  • 이 연산자는 피연산자의 각 자리(2진수로 표현했을 때)를 ‘오른쪽(>>)’ 또는 ‘왼쪽(<<)’으로 이동(shift)한다고 해서 ‘쉬프트 연산자(shift operator)라고 부른다.
  • [예제] 8 << 2 의 연산 과정
    1. 10진수 8은 2진수로 ‘00001000’
    2. ‘8 << 2’은 10진수 8의 2진수를 왼쪽으로 2자리 이동시킨다.
      → ‘00100000’ : 자리이동으로 인해 저장범위를 벗어난 값은 버려지고, 빈자리는 0으로 채워진다.
    3. ‘8 << 2’의 결과는 2진수로 ‘00100000’이 된다. (10진수로 32)
  • ‘<<’ 연산자의 경우, 피연산자의 부호에 상관없이 각 자리를 왼쪽으로 이동시키며 빈칸을 0으로 채움.
  • ‘>>’ 연산자의 경우, 오른쪽으로 이동시키기 때문에 부호있는 정수는 부호를 유지하기 위해 왼쪽 피연산자가 음수인 경우 빈자리를 1로 채우고 양수인 경우 빈자리를 0으로 채운다.
  • 쉬프트 연산자는 다른 이항연산자들과 달리 피연산자의 타입을 일치시킬 필요가 없기 때문에 우측 피연산자에는 산술변환이 적용되지 않는다.
  • [예제] 쉬프트 연산의 예
public class _18_ShiftOperator {
    //    10진 정수를 2진수로 변환하는 메서드
    static String toBinaryString(int x) {
        String zero = "00000000000000000000000000000000";
        String tmp = zero + Integer.toBinaryString(x);
        return tmp.substring(tmp.length()-32);
    }

    public static void main(String[] args) {
        int dec = 8;

        System.out.printf("%d >> %d = %4d \\t%s%n",
                dec, 0, dec >> 0, toBinaryString(dec >> 0));
        System.out.printf("%d >> %d = %4d \\t%s%n",
                dec, 1, dec >> 1, toBinaryString(dec >> 1));
        System.out.printf("%d >> %d = %4d \\t%s%n",
                dec, 2, dec >> 2, toBinaryString(dec >> 2));
        System.out.printf("%d << %d = %4d \\t%s%n",
                dec, 0, dec << 0, toBinaryString(dec << 0));
        System.out.printf("%d << %d = %4d \\t%s%n",
                dec, 1, dec << 1, toBinaryString(dec << 1));
        System.out.printf("%d << %d = %4d \\t%s%n",
                dec, 2, dec << 2, toBinaryString(dec << 2));
        System.out.println();

        dec = - 8;
        System.out.printf("%d >> %d = %4d \\t%s%n",
                dec, 0, dec >> 0, toBinaryString(dec >> 0));
        System.out.printf("%d >> %d = %4d \\t%s%n",
                dec, 1, dec >> 1, toBinaryString(dec >> 1));
        System.out.printf("%d >> %d = %4d \\t%s%n",
                dec, 2, dec >> 2, toBinaryString(dec >> 2));
        System.out.printf("%d << %d = %4d \\t%s%n",
                dec, 0, dec << 0, toBinaryString(dec << 0));
        System.out.printf("%d << %d = %4d \\t%s%n",
                dec, 1, dec << 1, toBinaryString(dec << 1));
        System.out.printf("%d << %d = %4d \\t%s%n",
                dec, 2, dec << 2, toBinaryString(dec << 2));
        System.out.println();

        dec = 8;
        System.out.printf("%d >> %2d = %4d \\t%s%n",
                dec, 0, dec >> 0, toBinaryString(dec >> 0));
        System.out.printf("%d >> %2d = %4d \\t%s%n",
                dec, 32, dec >> 32, toBinaryString(dec >> 32));
    }
}
// 결과
8 >> 0 =    8 	00000000000000000000000000001000
8 >> 1 =    4 	00000000000000000000000000000100
8 >> 2 =    2 	00000000000000000000000000000010
8 << 0 =    8 	00000000000000000000000000001000
8 << 1 =   16 	00000000000000000000000000010000
8 << 2 =   32 	00000000000000000000000000100000

-8 >> 0 =   -8 	11111111111111111111111111111000
-8 >> 1 =   -4 	11111111111111111111111111111100
-8 >> 2 =   -2 	11111111111111111111111111111110
-8 << 0 =   -8 	11111111111111111111111111111000
-8 << 1 =  -16 	11111111111111111111111111110000
-8 << 2 =  -32 	11111111111111111111111111100000

8 >>  0 =    8 	00000000000000000000000000001000
8 >> 32 =    8 	00000000000000000000000000001000

x << n 은 x * 2ⁿ의 결과와 같다.
x >> n 은 x / 2ⁿ의 결과와 같다.

 

  • 곱셈이나 나눗셈 연산자가 있는데 굳이 쉬프트 연산자를 사용하는 이유?
    속도가 빠름, 그러나 실행속도도 중요하지만 프로그램을 개발할 때 코드의 가독성(readability)도 중요함.
    쉬프트 연산자보다는 곱셈 또는 나눗셈 연산자를 주로 사용, 보다 빠른 실행속도가 요구되어지는 곳만 쉬프트 연산자를 사용하는 것이 좋다.
  • [예제] 10진수, 16진수에 쉬프트 연산자 사용
public class _19_ShiftOperator2 {
    public static void main(String[] args) {
        int dec = 1234;
        int hex = 0xABCD;
        int mask = 0xF;

        System.out.printf("hex=%X%n", hex); // hex=ABCD
        System.out.printf("%X%n", hex & mask); //

        hex = hex >> 4;
        System.out.printf("%X%n", hex & mask);

        hex = hex >> 4;
        System.out.printf("%X%n", hex & mask);

        hex = hex >> 4;
        System.out.printf("%X%n", hex & mask);
    }
}
// 결과
hex=ABCD
D
C
B
A

→ 쉬프트 연산자와 비트 ‘&’ 연산자를 이용해서 16진수를 끝에서부터 한자리씩 뽑아내는 예제

반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...