Skip to content

Programming with JS: Bitwise Operations

Dia Lee edited this page Jun 10, 2019 · 2 revisions

이 일련의 기사에서는 JavaScript의 prism과 다른 컴퓨터 과학 주제를 살펴봅니다. 우리는 이미 다양한 알고리즘을 알고있고 많은 배열들을 정렬했습니다. 근본적인 개념을 잊지 않는 것이 제일 중요한 것입니다.

비트로 알려진 1과 0 중 가장 근본적인 것을 의미합니다. 매일 코딩을 할 때 이 작업이 중요하지 않다고 생각해왔지만 결국은 가장 중요한 개념임을 깨닫습니다.

비트를 이해하지 못한다면 더 나은 JavaScript 개발자가 되지 않습니다. 작업 중인 React 앱에는 도움이 되지 않지만 일반적으로 더 나은 소프트웨어 개발자가 될 수 있습니다.

모든 것을 알 필요는 없으며 사실 그 모든 것을 기억하지 못할 것입니다. 이 글에서 저의 목표는 비트와 비트 조작에 대해 알아야 할 가장 기본적인 지식을 다루는 것입니다.


그렇다면, 비트가 뭔데?

컴퓨터에서는 모든 것이 1초와 0초로 내려갑니다. 숫자, 문자 또는 문자열과 함께 작동하지 않으며 이진수(비트)만 사용합니다. 모든 것이 이진 형식으로 저장된다는 것입니다. 그런 다음, 컴퓨터는 UTF-8과 같은 인코딩을 사용하여 저장된 비트 조합을 문자, 숫자 또는 다른 기호(ELI5 버전)에 맵핑합니다.

비트가 많으면 치환이 많아지고 많을수록 대표할 수 있는 것도 많아집니다. (뭔 의미인지 모르겠음)

예를 들어 113번을 예로 들어 보겠습니다. JS에서 이진 형식을 얻는 가장 쉬운 방법은 다음과 같습니다.

Number(113).toString(2) // 1110001

이 예시는 일부분에 불과합니다. 이제 우리가 그것들을 어떻게 조작할 수 있는지 살펴보도록 하겠습니다.

많은 블로그에서 16진수를 가진 예시를 가지고 있습니다. 여기서는 십진수 및 이진수만 살펴보겠습니다. 그 이면의 논리는 이것이 더 직관적으로 이해될 수 있다는 것입니다. 의심스러울 때는 기본적으로 종이에 비트 및 모든 작업을 기록하고 무슨 일이 일어나고 있는지 추적할 수 있습니다.

또한 JavaScript에서 직접 이진수를 입력할 수 있는 방법이 없습니다. 이진수를 십진수로 변환하려면 구문을 사용할 수 있습니다.

// function parseInt 
parseInt(1111, 2) //15

& (AND)

일별 프로그래밍 작업에서 이미 사용하고 있는 && 논리 연산자와 마찬가지로, 이 연산자는 비교된 비트가 모두 1과 0이면 다른 모든 경우에 1을 반환합니다. 양쪽에서 숫자를 선택한 다음(이진 형식이 아니라 숫자) 비트를 하나씩 비교합니다.

그것을 시각화해보죠. 숫자 121511001111의 이진수를 나타냅니다. 그 숫자에 & 연산자를 사용해보죠. 로그에 찍어보면 12만 결과로 도출이 됩니다. 뭔가 이상하죠?

12의 모든 비트를 15와 비교했고 운영방식에 따라 1100을 다시 얻었습니다. 그래서 12가 나온 것 입니다.

& 연산자가 갖고 있는 재미있는 작업 중 하나는 숫자가 짝수인지 홀수인지 알아내는 것입니다. 숫자가 홀수이면 첫 번째 비트는 항상 1이 됩니다. 따라서 숫자를 사용하고 1과 비교할 수 있으므로 숫자가 홀수이면 결과는 1이 됩니다. 하지만 실제 코드베이스에 이 코드를 사용하는 것은 권장하지 않습니다. 현재 무엇을 하고 있는지 명확하지 않기 때문입니다.


| (OR)

이것은 ||과 매우 유사합니다. 비교된 이진수가 모두 0일 때 적어도 10이 있는 각 비교에 대해 1을 반환하여 두 개의 이진수를 이진수 별로 비교하는 데 사용됩니다. 앞의 예를 들어 이 연산자를 사용하면 12 | 1515가 반환됩니다. 왜그럴까요?

1100 | 1111은 다시 1111 또는 15와 동일한 각 비교에 대해 1을 반환합니다.


~ (NOT)

이 것은 조금 더 현명한 NOT입니다. 결과는 2의 칭찬 산술에서 e 음수입니다. 즉, 1초에서 0초로 모든 비트를 되돌리고 그 반대의 경우도 마찬가지입니다.

그러나 ~15를 로그에 찍는다면 비트가 올바르더라도 결과가 -16이 됩니다. 이는 두 개의 보완 산술에서 숫자의 음수를 표시하려면 먼저 숫자의 비트를 바꾼 다음 1을 추가해야 하기 때문입니다.


^ (XOR)

이 연산자는 XOR 연산자 또는 독점적인 OR로 알려져 있습니다. &, | 연산자와 동일하게 비교를 하는 방식을 차별화하면서 양쪽의 숫자를 취합니다.

해당하는 비트를 비교하고 1개만 있을 때만 1을 반환합니다. 이 비트의 경우 설명하기에 그다지 좋지 않을 것입니다. 그러니 좀 더 시각적으로 설명하는 것이 좋겠군요, 1 ^ 01을 반환합니다. 그러나 1 ^ 10을 반환합니다.

^ 연산자는 10을 비교하는 특정 경우에만 1을 반환합니다.


Shifting 연산자

이동 비트를 처리하는 >>, << 연산자가 있습니다. 여러분이 추측할 수 있듯이 차이는 숫자의 비트를 이동하는 위치입니다.

<< 연산자는 숫자의 모든 비트를 n번 바꿉니다. 여기서 유의해야 할 것은 번호를 옮길 때 발생하는 빈 공간은 모두 0으로 채워져 있다는 점입니다.

반면에 >> 연산자는 오른쪽으로 이동합니다. 이 변환 연산자와 이전 변환 연산자의 차이점은 이 연산자가 양의 숫자의 비트를 0초로 채우고 음의 숫자의 비트를 1초로 채우는 것입니다.

여기서 짚고 넘어가야 할 부분이 있습니다. 보통 숫자의 첫 번째 부분은 표지를 나타내기 위해 사용됩니다. 1이면 음수이고, 0이면 양수입니다. 따라서 우파의 이면에 있는 추론은 우리가 움직이고 있는 숫자의 표식을 지키기 위한 것입니다.


비트연산자 응용해보기

이제 무엇을 하는지 알게 되었으니 비트를 조작하는 데 어떻게 활용할 수 있는지 살펴보겠습니다.

자, 이제 위치를 옮기도록 합시다. 오른쪽의 두 번째 비트를 설정합니다(1). 이것은 우리에게 masks 개념을 가져다 줍니다. masks는 달성하려는 항목에 따라 1 또는 0으로 설정할 비트만 있는 이진 형식의 숫자입니다. 또한 변경할 비트를 정의하는 플래그로도 사용된다고 말할 수 있습니다.

첫 번째 비트를 설정하려면 마스크가 0001이 됩니다. 두 번째 설정을 원하는 경우 0010 등이 됩니다.

예를 들어 다음과 같은 사항을 보다 명확하게 할 수 있습니다.

const setBit = (num, position) => {
  let mask = 1 << position
  return num | mask
}

// Set the bit at position 1
setBit(12, 1) // return 14 -> 1110

자, 지금까지는 좋습니다. 어떻게 하면 좀 더 정리할 수 있는지 알아보겠습니다. 비트를 0으로 설정해보겠습니다. 다른 모든 비트는 그대로 유지해야 하기 때문에 이전 예처럼 쉽지 않습니다.

const clearBit = (num, position) => {
  // We use the ~/NOT operator after placing the bit
  // We want 1s everywhere and 0 only where we want to modify
  let mask = ~(1 << position)
  
  // We use AND which will modify only the bits compared to 0
  return num & mask
}

clearBit(15, 1) // 12 -> 1100

여기서 다른 점은 mask가 1s로 가득 차 있고 지우려는 위치에서만 0을 갖기를 원한다는 점입니다. 그런 다음 필요한 위치만 0으로 설정하는 & 연산자를 사용합니다.

이제 비트 설정 및 삭제 방법을 알 수 있습니다. 하지만 뒤집으려면 어떻게 해야 할까요? 우리는 그 비트가 설정되었는지 아닌지 알 수 없지만, 우리는 정말로 그것의 현재 상태를 바꾸기를 원합니다. 이건 XOR의 일입니다.

const flipBit = (num, position) => {
  let mask = 1 << position
  // If the current state of the bit is 0, XOR will return 1
  // If the bit is 1, XOR will set it to 0
  return num ^ mask
}

flipBit(15, 1) // 13 -> 1101

이 경우 XOR는 1에 1을 사용하는 것이 비트의 값을 뒤집을 수 있기 때문에 사용됩니다.


Clone this wiki locally