-
Notifications
You must be signed in to change notification settings - Fork 13
Programming with JS: Bitwise Operations
이 일련의 기사에서는 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
일별 프로그래밍 작업에서 이미 사용하고 있는 &&
논리 연산자와 마찬가지로, 이 연산자는 비교된 비트가 모두 1과 0이면 다른 모든 경우에 1을 반환합니다. 양쪽에서 숫자를 선택한 다음(이진 형식이 아니라 숫자) 비트를 하나씩 비교합니다.
그것을 시각화해보죠. 숫자 12
와 15
는 1100
과 1111
의 이진수를 나타냅니다. 그 숫자에 &
연산자를 사용해보죠. 로그에 찍어보면 12만 결과로 도출이 됩니다. 뭔가 이상하죠?
12
의 모든 비트를 15
와 비교했고 운영방식에 따라 1100
을 다시 얻었습니다. 그래서 12
가 나온 것 입니다.
&
연산자가 갖고 있는 재미있는 작업 중 하나는 숫자가 짝수인지 홀수인지 알아내는 것입니다. 숫자가 홀수이면 첫 번째 비트는 항상 1
이 됩니다. 따라서 숫자를 사용하고 1
과 비교할 수 있으므로 숫자가 홀수이면 결과는 1
이 됩니다. 하지만 실제 코드베이스에 이 코드를 사용하는 것은 권장하지 않습니다. 현재 무엇을 하고 있는지 명확하지 않기 때문입니다.
이것은 ||
과 매우 유사합니다. 비교된 이진수가 모두 0
일 때 적어도 1
과 0
이 있는 각 비교에 대해 1
을 반환하여 두 개의 이진수를 이진수 별로 비교하는 데 사용됩니다. 앞의 예를 들어 이 연산자를 사용하면 12 | 15
가 15
가 반환됩니다. 왜그럴까요?
1100 | 1111
은 다시 1111
또는 15
와 동일한 각 비교에 대해 1
을 반환합니다.
이 것은 조금 더 현명한 NOT
입니다. 결과는 2의 칭찬 산술에서 e 음수입니다. 즉, 1초에서 0초로 모든 비트를 되돌리고 그 반대의 경우도 마찬가지입니다.
그러나 ~15
를 로그에 찍는다면 비트가 올바르더라도 결과가 -16
이 됩니다. 이는 두 개의 보완 산술에서 숫자의 음수를 표시하려면 먼저 숫자의 비트를 바꾼 다음 1을 추가해야 하기 때문입니다.
이 연산자는 XOR 연산자 또는 독점적인 OR로 알려져 있습니다. &
, |
연산자와 동일하게 비교를 하는 방식을 차별화하면서 양쪽의 숫자를 취합니다.
해당하는 비트를 비교하고 1개만 있을 때만 1을 반환합니다. 이 비트의 경우 설명하기에 그다지 좋지 않을 것입니다. 그러니 좀 더 시각적으로 설명하는 것이 좋겠군요, 1 ^ 0
이 1
을 반환합니다. 그러나 1 ^ 1
은 0
을 반환합니다.
^
연산자는 1
과 0
을 비교하는 특정 경우에만 1
을 반환합니다.
이동 비트를 처리하는 >>
, <<
연산자가 있습니다. 여러분이 추측할 수 있듯이 차이는 숫자의 비트를 이동하는 위치입니다.
<<
연산자는 숫자의 모든 비트를 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을 사용하는 것이 비트의 값을 뒤집을 수 있기 때문에 사용됩니다.
일부 의역이 들어간 경우도 있으므로 해당 원문의 내용과 조금 다를 수 있습니다.
문제가 될 소지가 있다거나 혹은 수정이 필요한 사항이 있다면 있다면 issues 보내주세요.
기술문서
- 호출스택
- 원시자료형
- 값타입과 참조타입
- 명시적 변환, 암시적 변환, Nominal, 구조화, 덕 타이핑
- == vs === vs typeof
- 함수 범위, 블록 범위, 렉시컬(lexical) 범위
- 식(expression) vs 문(statement)
- IIFF, Modules, Namespaces
- 메세지큐와 이벤트루프
- setTimeout, setInterval, requestAnimationFrame
- 자바스크립트 엔진
- 비트 연산자, 형식화 배열, 버퍼(배열)
- DOM과 Layout Trees
- 팩토리와 클래스
- this, call, apply, bind
- new, 생성자, instanceof, 인스턴스
- 프로토타입의 상속과 체인
- Object.create와 Object.assign
- map, reduce, filter
- 순수함수, 부수효과, 상태변이
- Closure
- 고차함수
- 재귀
- 컬렉션과 생성기
- Promise
- async, await
- 자료구조
- 함수 성능과 빅 오 표기법
- 알고리즘
- 상속, 다형성, 코드의 재사용성
- 설계패턴
- 부분 어플리케이션, 커링, Compose, Pipe
- 클린코드