[Java] 비트 연산자 / 시프트 연산자

by Blogger 하얀쿠아
2012. 2. 2. 10:45 소프트웨어 Note/Java

[Java] 비트 연산자 / 시프트 연산자


자바언어에는 C/C++ 와 동일하게 비트연산자들인 '&', '|', '^', '~' 와 시프트연산자인 '<<' 와 '>>' 가 있습니다.

그러나 자바에만 있는 연산자가 있는데요.

바로  '>>>' 라는 연산자가 추가적으로 존재합니다.

순서대로 정리해 보도록 하겠습니다.


비트연산자

컴퓨터 내부의 데이터를 비트 단위로 비교하거나 조작할 때 사용한다.


1. 논리곱 (and) &

각 비트를 비교하여 양쪽 모두 1이면 1, 아니면 0을 반환함.


ex) a = 110, b= 220

a = 0 1 1 0 1 1 1 0

b = 1 1 0 1 1 1 0 0 

a&b =  0 1 0 0 1 1 0 0


2. 논리합 (or) |

각 비트를 비교하여 어느 한쪽이 1 이면 1, 그렇지 않으면 0을 반환함.


ex) a = 110, b= 220

a = 0 1 1 0 1 1 1 0

b = 1 1 0 1 1 1 0 0 

a|b =  1 1 1 1 1 1 1 0


3. 배타적 논리합(xor) ^

각 비트를 비교하여 한쪽이 1이고 다른 한쪽이 0이면 1을, 아니면 0을 반환함.


ex) a = 110, b= 220

a = 0 1 1 0 1 1 1 0

b = 1 1 0 1 1 1 0 0 

a^b =  1 0 1 1 0 0 1 0 


4. 1의 보수 표현(not) ~

각 비트를 반전시킨 값을 반환함.


ex) a = 110

a = 0 1 1 0 1 1 1 0

~a =  1 0 0 1 0 0 0 1



시프트 연산자

비트 열을 좌우로 지시한 만큼 이동시키는(shift) 연산자를 말합니다.
본문 처음에 말했듯이, C/C++ 에는 >> 와 << 만  존재하지만, 자바에는 추가적으로 '>>>' 연산자가 있습니다.

1. 왼쪽 시프트 연산자 <<

ex) 150 << 2
150 의 이진값을 왼쪽으로 2칸 시프트 합니다.
 
     1 0 0 1 0 1 1 0 : 150
1 0 0 1 0 1 1 0 0 0 : 600

위와 같은 결과. 왼쪽으로  두칸 밀면서, 비게 되는 오른쪽 두칸은 0으로 채웁니다.
만약 데이터를 담는 자료형이 byte 타입이었다면, (8bit) 왼쪽으로 밀린 2개의 비트는 삭제됩니다.

그런 경우 다음과 같은 결과가 됩니다
    0 1 0 1 1 0 0 0 :  88
 

2. 오른쪽 시프트 연산자 >> 

ex) 150 >> 2
150의 이진값을 오른쪽으로 2칸 시프트 합니다.

    1 0 0 1 0 1 1 0
    1 1 1 0 0 1 0 1 1 0

오른쪽으로 2비트 이동 한 후, 비게되는 왼쪽의 2개비트는 1로 채워지고, 오른쪽에서 2비트 넘어간 부분은 삭제됩니다. 
따라서 결과는 아래가 됩니다.
    1 1 1 0 0 1 0 1 

주의점 : 무조건 왼쪽에 비는 부분이 1로 채워지는 것이 아닙니다. -> 밀기전 최초 첫째짜리 값(MSB)과 동일한 값으로 채워집니다.


3. 논리 오른쪽 시프트 연산자 >>>

150 >>> 2
오른쪽으로 2비트 시프트 합니다.

자바에 추가된 논리 시프트는 오른족으로 밀면서 비게되는 앞쪽 비트를 무조건 0으로 채워넣는 것이다.

    1 0 0 1 0 1 1 0
    0 0 1 0 0 1 0 1 1 0

으로 되는 것. 비게 되는 왼쪽 2비트가 무조건 0 으로 채워집니다.
밀려난 오른쪽 2개비트 1 0 은 삭제됩니다.


G.711 코덱 구현을 하면서 비트연산자와 시프트 연산자를 사용할 일이 꽤 많았어요.
그런데 몇 몇 연산자의 용법을 자주 혼동하게 되어서, 다시 한번 정리를 해봅니다.


즐거운 코딩 하세요!



이 댓글을 비밀 댓글로
    • 알롱싸
    • 2012.08.07 17:28
    왼쪽 시프트 연산자 결과값 (1 1 1 0 0 1 0 1) 은 어떻게 나오는거에요?
    • 왼쪽 시프트 연산자가 아니라 오른쪽 시프트 연산자 에서의 예제를 말씀하신 것 같이 보이는 군요.

      10010110 에서 오른쪽으로 2칸을 밀게 되면 MSB(제일 왼쪽에 있는 숫자)인 1과 같은 숫자로 2개가 채워지고 밀린 숫자는 없어집니다.
      ->> 이렇게 2칸 밀면
      즉, [11]100101(10) 이렇게 되겠죠
      [ ] 안이 채워진 숫자이고, ( ) 안이 없어진 숫자 입니다.

[Java/Tip] 자바에서 unsigned byte 다루기.

by Blogger 하얀쿠아
2012. 2. 2. 10:12 소프트웨어 Note/Java
자바의 Primitive Data Type 들 중에는 unsigned 데이터 타입이 존재하지 않습니다.

unsigned를 특별히 사용할 일이 없을때는 의식하지 못하고 있었는데, 자바에서(정확히는 안드로이드에서 사용할 목적으로) 음성을 G.711 인코딩/디코딩 하기 위한 코덱을 구현 하던 중 깨닫게 되었습니다.

byte 타입을 unsigned 로 0~255 을 사용하고 싶었지만, 자바에서는 unsigned 키워드 자체가 존재하지 않아 byte 타입은 -128~127 의 값을 갖게 되어 있더군요. 즉 2's complement 의 값입니다.

하지만 제가 필요한 것은 0~255 의 범위값이었습니다. 
정확히는, C#에서 byte 타입으로 보낸 0~255의 값 (이진 값으로는 0000 0000 ~ 1111 1111 이 되겠죠) 을 자바에서도 0~255로 해석하도록 해야 했습니다.

그래서 이를 해결하기 위해 꼼수를 생각해 본 결과..

다음과 같이 하여 해결 할 수 있었습니다. 

byte value = xxx;
int nValue = 0;

if (value < 0)
 nValue = (int)value + 256;
else
 nValue = (int)value;

결과적으로 nValue에는 byte 값을 unsigned 로 변환한 값이 들어가게 됩니다.
원리는 간단하죠. 
0~127 값은 그대로 사용하면 됩니다. 2진수로 표현할 경우 0XXX XXXX 가 되기 때문이죠.
문제가 되는 -128~-1  은 2진수로 표현할 경우 1XXX XXXX 가 되며, MSB(most significant bit) 가 1이어서 어떻게 해석하느냐에 따라 표현되는 값이 달라지게 됩니다.  즉 unsigned 면 이를 128~255 로 표현 하겠죠. signed 면 2's complement 부호비트로 해석하여 -128 ~ -1 로 해석하는 것이죠.

결론은 -128 ~ -1 에 256을 더해주면, 됩니다.


 
이 댓글을 비밀 댓글로
    • 진짜궁금해서물어봄
    • 2019.02.21 21:08
    그냥 처음부터 byte에 128을 더하면 되지않나요??
      • ^^
      • 2019.02.27 15:06
      그러면 0~127을 표현할 수가 없지요~

      if를 사용하지 않고 싶다면
      int nValue = (int)value & 0xff 를 하시면 됩니다.

      1000 0000 인 byte 값을 int로 변환하면
      1111 1111 1111 1111 1111 1111 1000 0000 이 되고, 여기에
      0000 0000 0000 0000 0000 0000 1111 1111 을 and 연산해주면
      0000 0000 0000 0000 0000 0000 1000 0000 이 되어 -128이 아닌 128이 됩니다.

      자바의 거지같은 점이죠..ㅠㅠ
    • 박형준
    • 2019.06.11 12:10
    다른환경에서 암호화된 문자열 복호화 하는 과정에서 이 문제가 의심되서 검색해봤는데, 이런꿀팁이 있었다니 ㅜㅜ
    잘보고 갑니다

[Java] 정규표현식 표현 방법

by Blogger 하얀쿠아
2011. 12. 19. 19:45 소프트웨어 Note/Java

java.util.regex 패키지에 있는 Match 클래스와 Pattern 클래스를 사용하여 문자열을 정규표현식으로 검증할 수 있다.

boolean bln = Pattern.matches("^[a-zA-Z0-9]*$", this.input);


정규표현식은 다음과 같은 문법으로 되어 있다.

 ^ : 문자열의 시작을 나타냄.

$ : 문자열의 종료를 나타냄. \

. : 임의의 한 문자를 나타냄. (문자의 종류는 가리지 않는다)

| : or를 나타냄.

? : 앞 문자가 없거나 하나있음을 나타냄.

+ : 앞 문자가 하나 이상임을 나타냄.

* : 앞 문자가 없을 수도 무한정 많을 수도 있음을 나타냄. 
 만약, .* 으로 정규식이 시작한다면 시작하는 문자열과 같은 문자열이 뒤에 없거나 많을 수도 있는 경우에만 일치를 시킨다. 즉, abc 일 경우 시작문자인 a를 기준으로 a가 없을경우와 a가 무한정 많은 경우에도 true를 반환하기 때문에 abc의 경우는 true를 반환한다.

[] : 문자 클래스를 지정할 때 사용한다. 문자의 집합이나 범위를 나타내면 두 문자 사이는 '-' 기호로 범위를 나타낸다. []내에서 ^ 가 선행하여 나타나면 not 를 나타낸다.

{} : 선행문자가 나타나는 횟수 또는 범위를 나타낸다.
a{3} 인 경우 a가 3번 반복된 경우를 말하며, a{3,}이면 a가 3번 이상 반복인 경우를 말한다. 또한 a{3,5}인 경우 a가 3번 이상 5번 이하 반복된 경우를 나타낸다.

\w : 알파벳이나 숫자
\W : 알파벳이나 숫자를 제외한 문자
\d : 숫자 [0-9]와 동일
\D : 숫자를 제외한 모든 문자

위의 내용을 활용하여 다음 몇가지 예제를 만들어 볼 수 있다.

- 기본적인 문자열 검증 정규식
^[0-9]*$  :  숫자만
^[a-zA-Z]*$  :  영문자만
^[가-힣]*$  :  한글만
^[a-zA-Z0-9]*$  :  영어/숫자만

- 정규식 표현 예제

이메일 : ^[a-zA-Z0-9]+@[a-zA-Z0-9]+$  or  ^[_0-9a-zA-Z-]+@[0-9a-zA-Z-]+(.[_0-9a-zA-Z-]+)*$

휴대폰 :  ^01(?:0|1|[6-9]) - (?:\d{3}|\d{4}) - \d{4}$

일반전화 : ^\d{2,3} - \d{3,4} - \d{4}$

주민등록번호 : \d{6} \- [1-4]\d{6}

IP 주소 : ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3})

이 댓글을 비밀 댓글로

부분문자열을 얻는 방법 - Java와 SQL

by Blogger 하얀쿠아
2010. 11. 6. 21:11 소프트웨어 Note/Java


JAVA의 String 메소드중 하나인 Substring 은 부분문자열을 얻을때 사용한다.

Substring(int beginIndex, int endIndex) 의 형태를 가진다.
beginIndex 부터 endIndex 까지를 부분문자열로 취하겠다는 뜻이다.
(endIndex의 문자는 제외가 된다!!)

예)
String str = "19000101-1234567"
str.substring(10, 17) 하면 1234567 을 부분문자열로 반환한다. (10부터 16까지 7자리를 읽어온다)

 

SQL에서는 부분문자열을 얻는 substr 라는게 있다.
형태는 substr(string S,int N1,int N2) 이다.
뭐냐하면 문자열 S를 N1 번째 문자부터 시작해서 N2 개 만큼 부분문자열로 취한다는 것이다.

예)
select substr("19000101-1234567", 10, 7) from dual
이렇게 하면 주어진 문자열 19000101-1234567 의 10번째부터 7개의 문자를 부분문자열로 취한다.
즉 1234567을 얻는다.


 

이 댓글을 비밀 댓글로