비트 연산자는 무엇입니까? [닫은]


19

프로그래밍 언어에는 종종 다양한 비트 연산자 (예 : 비트 왼쪽 및 오른쪽 시프트, 비트 AND, OR, XOR ...)가 있습니다. 이것들은 많이 사용되지는 않지만 적어도 내 경험이 있습니다. 때로는 프로그래밍 과제 나 인터뷰 질문에 사용되거나 다음과 같은 해결책이 필요합니다.

  • 항등 연산자를 사용하지 않고 true두 값이 같을 때 반환하는 함수를 만듭니다.
  • 세 번째 변수를 사용하지 않고 두 변수의 값을 바꾸십시오

이것들은 아마도 실제 사용 이 거의 없을 것입니다 . 메모리를 직접 낮은 수준에서 조작하기 때문에 더 빠를 것 같아요.

왜 대부분의 프로그래밍 언어에서 그러한 것들이 발견됩니까? 실제 사용 사례가 있습니까?


@Anto-쉬운 예는 256Kb 상당의 데이터를 한 번에 256 워드 (4096 바이트)의 속도로 클라이언트에 보내는 것입니다.
Ramhound

1
"어떤 항등 연산자를 사용하지 않고, 두 값이 동일한 경우 true를 반환하는 함수 작성"- C에서을 : return !(x-y);? 난 몰라
앤드류 아놀드

@Andrew : 솔루션이지만 비트 연산자로도 가능합니다.
Anto

19
"이것들은 많이 사용되지는 않습니다." -확실합니까? 나는 항상 그들을 사용합니다. 우리는 모든 문제 영역에서 작동하지 않습니다.
Ed S.

2
완전한 대답을하기에는 충분하지 않지만 비트를 모으지 않고 바이트의 상위 4 비트를 읽은 다음 일부 데이터 형식이 매우 밀접하게 압축되어 있다고 생각하십시오 .
복원 Monica Monica

답변:


53

아니요, 실제 응용 프로그램이 많으며 컴퓨터의 기본 작업입니다.

그들은 사용됩니다

  • 프로그래밍 언어 데이터 유형에 맞지 않는 저글링 바이트 블록
  • 인코딩을 빅 엔디안에서 리틀 엔디안으로 전환합니다.
  • 직렬 또는 USB 연결을 위해 4 개의 6 비트 데이터를 3 바이트로 패킹
  • 많은 이미지 형식에는 각 색상 채널에 할당 된 비트 수가 다릅니다.
  • 임베디드 애플리케이션에서 IO 핀과 관련된 모든 것
  • 8 비트 경계에 적합한 데이터가없는 데이터 압축. \
  • 해싱 알고리즘, CRC 또는 기타 데이터 무결성 검사
  • 암호화
  • 슈도 랜덤 수 생성
  • RAID 5는 볼륨간에 비트 XOR을 사용하여 패리티를 계산합니다.
  • 더 톤

실제로 논리적으로 컴퓨터의 모든 작업은 결국 프로세서의 전기 게이트 내에서 발생하는 이러한 낮은 수준의 비트 단위 작업의 조합으로 귀결됩니다.


1
추가하려는 것처럼 보이는 종합적인 목록에 +1
Anto

28
+1. @Anto :이 목록은 거의 어디에도 없습니다 . 시스템 프로그래밍에서 비트 연산자에 대한 포괄적 인 사용 사례 목록은 비즈니스 응용 프로그램의 SQL 쿼리에 대한 포괄적 목록만큼 길어야합니다. 재미있는 사실 : 저는 비트 단위 연산을 항상 사용하지만 몇 년 안에 SQL 문을 작성하지 않았습니다. ;-)
nikie

4
@ nikie : 그리고 항상 SQL을 작성하지만 몇 년 동안 비트 연산자를 사용하지 않았습니다! :)
FrustratedWithFormsDesigner

3
나는 임베디드 시스템에서 일한다-비트 연산자는 빵과 버터 재료이다. 전혀 생각하지 않고 매일 사용합니다.
quick_now

9
때때로 SQL에서 비트 시프 팅을 사용하면 상을 받습니까?
Ant

13

그것들은 근본적인 작업이기 때문입니다.

같은 생각으로, 덧셈 은 뺄셈 (및 부정)과 곱셈으로 완전히 대체 될 수 있기 때문에 덧셈 은 실제 용도가 거의 없다고 주장 할 수 있습니다. 그러나 우리는 기본 운영 이기 때문에 추가를 유지 합니다.

비트 연산이 많이 필요하지 않다고해서 자주 사용되지 않는 것은 아닙니다. 실제로 비트 마스킹과 같은 것에 사용했던 거의 모든 언어에서 비트 단위 연산을 사용했습니다.

머리 꼭대기에서 나는 이미지 처리, 비트 필드 및 플래그, 텍스트 처리 (예 : 특정 클래스의 모든 문자가 공통 비트 패턴을 공유하는 경우), 직렬화 된 데이터 인코딩 및 디코딩, VM 또는 CPU 디코딩에 비트 단위 연산을 사용했습니다. opcode 등. 비트 단위 연산이 없으면 이러한 작업의 대부분은 작업의 안정성을 떨어 뜨리거나 가독성이 떨어지기 위해 여러 번 더 복잡한 작업이 필요합니다.

예를 들면 다음과 같습니다.

// Given a 30-bit RGB color value as a 32-bit int
// A lot of image sensors spit out 10- or 12-bit data
// and some LVDS panels have a 10- or 12-bit format
b = (color & 0x000003ff);
g = (color & 0x000ffc00) >> 10;
r = (color & 0x3ff00000) >> 20;

// Going the other way:
color = ((r << 20) & 0x3ff00000) | ((g << 10) & 0x000ffc00) | (b & 0x000003ff);

RISC 유형 CPU (예 : 다른 플랫폼을 에뮬레이션 할 때)에 대한 CPU 명령어를 디코딩하려면 위와 같이 큰 값을 추출해야합니다. 때로는 곱셈과 나눗셈, 모듈로 등으로 이러한 연산을 수행하는 것이 동등한 비트 연산보다 10 배 느릴 수 있습니다.


12

일반적인 예는 24 비트 RGB 값에서 개별 색상을 추출하는 것입니다.


편집 : http://www.docjar.com/html/api/java/awt/Color.java.html에서

    value =  ((int)(frgbvalue[2]*255 + 0.5))    |
                (((int)(frgbvalue[1]*255 + 0.5)) << 8 )  |
                (((int)(frgbvalue[0]*255 + 0.5)) << 16 ) |
                (((int)(falpha*255 + 0.5)) << 24 );

실제로이 예를 보여 주겠습니까? 코드 스 니펫?
Anto

더 좋은 예는 16 비트 (4.5bpc) 또는 30 비트 (10bpc) RGB 값을 처리하는 것입니다.
greyfade

@ grey, 그러한 예제를 자유롭게 추가하십시오.

6

다음 은 Quake 3, Quake 4에서 찾을 수 있는 실제 예 입니다. Doom III. Q3 엔진을 사용한 모든 게임 .

float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking [sic]
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck? [sic]
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//    y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

        return y;
}

(이 코드를 이해하려면 부동 소수점 숫자가 저장되는 방법을 이해해야합니다. 정확하게 설명 할 수는 없습니다)

사용 측면에서 네트워킹이나 그래픽과 같은 비트 이동이 필요한 필드에 있지 않으면 목적이 약간 학문적 일 수 있습니다. 그러나 여전히 흥미 롭습니다.


+1 귀하의 의견이 아닌 경우에도 해당 의견에 대해 날 멍청하게 만들었 어
Bassinator

4

쉬프팅은 2의 거듭 제곱이나 나누기보다 빠릅니다. 예를 들어, << = 2는 a에 4를 곱합니다. 반대로 a >> = 2는 a를 4로 나눕니다. 비트 단위 연산자를 사용하여 장치에 데이터를 비트 뱅킹 할 수도 있습니다. 예를 들어, N 루프 내에서 shift, xor 및 "and"연산을 사용하여 N 핀 포트에서 N 개의 직렬 데이터 스트림을 보낼 수 있습니다. 디지털 로직에서 달성 할 수있는 모든 것은 소프트웨어에서 수행 될 수 있으며 그 반대도 마찬가지입니다.


1
올림 또는 내림 등으로 나눌 때주의하십시오. 시프 팅은 그 점을 설명하지 않으므로 나누기를 의미 할 때 코드에서 나누기를 사용하고 컴파일러가 쉬프트로 최적화하고 추가하도록하는 것이 실제로 더 좋습니다 나를 위해.
Daemin

@ Daemin :이 기술을 사용할 때 정수로 작업하고 있습니다. C 및 C ++에서 정수 나누기의 기본 동작은 0에 대한 잘림입니다. 따라서 정수를 2의 거듭 제곱으로 오른쪽으로 시프트하면 정수를 2의 거듭 제곱으로 나누는 것과 동일한 결과가 생성됩니다.
bit-twiddler

1
@ bit-twiddler 오른쪽 시프트는 음수 나누기와 같은 방식으로 작동하지 않습니다.
Daemin

@Daemin : 당신은 저를 틀리게 증명하는데 지옥에 빠져있는 것 같습니다. 먼저 반올림 문제를 해결합니다. C와 C ++의 나누기가 0쪽으로 잘린다는 것을 말함으로써 그 주장을 거부하면 부호있는 정수 문제가 발생합니다. 시프트 연산자를 적용하여 2의 보수 음수 정수를 어디에 적용한다고 말했습니까? 그럼에도 불구하고 여전히 시프트 연산자를 사용하여 2의 거듭 제곱으로 나눌 수 있습니다. 그러나 C와 C ++는 평범한 이전 오른쪽 시프트 대신 산술 오른쪽 시프트를 수행하므로 먼저 값이 음수인지 확인해야합니다. 값이 음수이면
비트 트위 들러

1
정확하게, 미묘한 차이가 있으므로 곱셈과 나눗셈의 대용으로 시프트를 사용할 때주의하십시오. 그 이상도 이하도 아닌.
Daemin April

3

오래 전부터 비트 연산자가 유용했습니다. 오늘날에는 그렇지 않습니다. 아, 그것들은 완전히 쓸모는 없지만, 사용해야 했던 것을 본 이후 오랜 시간이 걸렸습니다 .

1977 년에 저는 어셈블리 언어 프로그래머였습니다. 나는 어셈블러가 유일한 진정한 언어라고 확신했다. 나는 파스칼과 같은 언어가 무엇이든 얻을 없었 학술 싫은 사람을 위해 있다고 확신했다 진짜 다.

그런 다음 Kernighan과 Ritchie의 "C Programming Language"를 읽습니다. 내 마음이 완전히 바뀌었다. 이유? 비트 연산자가있었습니다! 그것은 이었다 어셈블리 언어! 방금 다른 구문을 사용했습니다.

그 당시에는 ands, ors, shift 및 rotation없이 코드를 작성할 수 없었습니다. 요즘 나는 거의 사용하지 않습니다.

따라서 귀하의 질문에 대한 짧은 대답은 "아무것도 없습니다"입니다. 그러나 그것은 공평하지 않습니다. 그래서 더 긴 대답은 "대부분 아무것도 아닙니다"입니다.


xkcd.com/378 오릅니다.
Maxpm

비트 연산자는 오늘날 매우 유용합니다. 도메인에서 사용되지 않는다고해서 사용되지 않거나 자주 사용되지는 않습니다. 다음은 간단한 예입니다. 비트 연산자없이 AES를 시도하고 구현하십시오. 이것은 대부분의 컴퓨터에서 매일 수백 또는 수천 번 매일 수행되는 일에 대한 설명입니다.
저의 정확한 의견

비트 단위 연산자를 사용하지 않고 데이터를 인코딩 / 디코딩하는 것은 고통 스럽습니다. 예를 들어, MIME 첨부 파일을 메시지에 추가하려면 3-4 개의 데이터 코딩 (일명 radix64 코딩)을 처리 할 수 ​​있어야합니다.
bit-twiddler

2

암호화

DES 암호화 알고리즘 에서 매우 작은 스 니펫을 살펴 보는 것이 좋습니다 .

temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);

요즘 DES가 권장되지는 않지만 : P
Armand

@Alison : 아니요. 그러나이를 대체 한 암호화 알고리즘에는 더 많은 비트 조작 작업이 필요합니다. :-)
Carson63000

@Alison-물론 TripleDES는 3 비트의 키 비트로 3 번 DES를 수행합니다.
Scott Whitlock

2

좋은 답변이 많으므로 반복해서 사용하지 않겠습니다.

나는 관리 코드 (C # / .Net)에서 상당히 많이 사용하며 공간 절약, 고성능 또는 영리한 비트 시프 팅 알고리즘과는 아무런 관련이 없습니다. 때때로 일부 논리는 이러한 방식으로 데이터를 저장하는 데 적합합니다. 열거 형이있을 때 종종 사용하지만 인스턴스는 해당 열거 형에서 여러 값을 동시에 취할 수 있습니다. 직장에서 코드 예제를 게시 할 수는 없지만 "Flags 열거 형"( "Flags"는 비트 방식으로 사용될 열거 형을 정의하는 C # 방식)에 대한 빠른 Google이 좋은 예를 제공합니다. http : // www.dotnetperls.com/enum-flags .


2

비트 병렬 컴퓨팅도 있습니다. 데이터가 1과 0 인 경우 64 개를 부호없는 긴 단어로 묶고 64way 병렬 연산을 수행 할 수 있습니다. 유전자 정보는 2 비트 (DNA의 AGCT 인코딩을 나타냄)이며, 비트 병렬 방식으로 다양한 계산을 수행 할 수있는 경우 그렇지 않은 경우보다 더 많은 작업을 수행 할 수 있습니다. 메모리, 디스크 용량 또는 통신 대역폭이 제한된 경우 메모리의 데이터 밀도는 말할 것도없고 압축 / 압축 풀기를 고려해야합니다. 이미지 처리와 같은 영역에 나타나는 낮은 precison 정수조차도 까다로운 비트 병렬 컴퓨팅을 활용할 수 있습니다. 그 자체로 전체 예술입니다.


1

왜 찾습니까?

아마도 그것들은 어셈블리 명령어에 해당하고 때로는 고급 언어의 것들에 유용하기 때문일 것입니다. 조립 명령에 GOTO해당하는 공포에 대해서도 마찬가지입니다 JMP.

그들의 용도는 무엇입니까?

실제로 이름을 짓는 데는 많은 용도가 있으므로 최근에는 고도로 지역화 된 사용법을 알려 드리겠습니다. 나는 6502 어셈블리로 많은 일을했고 메모리 주소, 값, 비교 값 등을 GameGenie 장치 (기본적으로 NES의 치트 응용 프로그램)에 사용할 수있는 코드로 변환하는 작은 응용 프로그램을 작업하고있었습니다. 코드는 약간의 비트 조작으로 생성됩니다.


1

요즘 많은 프로그래머들은 거의 무한한 메모리를 가진 컴퓨터에 익숙합니다.

그러나 일부 사용은 여전히 ​​모든 비트 수 (예 : RAM이 1k 이하인 경우)가있는 초소형 마이크로 컨트롤러를 프로그래밍하며, 비트 연산자는 프로그래머가 훨씬 더 큰 프로그래밍을 낭비하는 대신 한 번에 하나씩 해당 비트를 사용할 수 있도록합니다. 알고리즘에 필요한 상태를 유지하는 데 필요할 수있는 추상화 엔티티. 이러한 장치의 IO는 비트 단위로 읽거나 제어해야 할 수도 있습니다.

"실제 세계"에는 서버 나 PC보다 훨씬 작은 마이크로 컨트롤러가 있습니다.

순수한 이론적 CS 유형의 경우, 튜링 머신은 모두 약간의 상태입니다.


1

비트 연산자의 많은 가능한 용도 중 하나 이상 ...

비트 연산자는 코드를 더 읽기 쉽게 만들 수 있습니다. 다음 함수 선언을 고려하십시오 ....

int  myFunc (bool, bool, bool, bool, bool, bool, bool, bool);

...

myFunc (false, true, false, false, false, true, true, false);

코드를 쓰거나 읽을 때 어떤 부울 매개 변수가 무엇을 의미하는지 잊어 버리는 것은 매우 쉽습니다. 계산을 추적하기도 쉽습니다. 이러한 루틴을 정리할 수 있습니다.

/* More descriptive names than MY_FLAGx would be better */
#define MY_FLAG1    0x0001
#define MY_FLAG2    0x0002
#define MY_FLAG3    0x0004
#define MY_FLAG4    0x0008
#define MY_FLAG5    0x0010
#define MY_FLAG6    0x0020
#define MY_FLAG7    0x0040
#define MY_FLAG8    0x0080

int  myFunc (unsigned myFlags);

...

myFunc (MY_FLAG2 | MY_FLAG6 | MY_FLAG7);

더 설명적인 플래그 이름을 사용하면 훨씬 더 읽기 쉽습니다.


1

유니 코드 에 대해 알고 있다면 UTF-8에 익숙 할 것입니다. 20 비트 코드 포인트를 1 ~ 4 바이트로 압축하기 위해 많은 비트 테스트, 시프트 및 마스크를 사용합니다.


0

나는 자주 사용하지 않지만 때로는 편리합니다. 열거 형 처리 가 마음에 듭니다 .

예:

enum DrawBorder{None = 0, Left = 1, Top = 2, Right = 4, Bottom = 8}

DrawBorder drawBorder = DrawBorder.Left | DrawBorder.Right;//Draw right & left border
if(drawBorder & DrawBorder.Left == DrawBorder.Left)
  //Draw the left border
if(drawBorder & DrawBorder.Top == DrawBorder.Top)
  //Draw the top border
//...

0

이 사용법이 아직 기록되어 있는지 확실하지 않습니다.

여러 반환 값을 0 또는 1로 줄이기 위해 illumos (openSolaris) 소스 코드로 작업 할 때 OR이 상당히 많이 보입니다.

int ret = 0;
ret |= some_function(arg, &arg2); // returns 0 or 1
ret |= some_other_function(arg, &arg2); // returns 0 or 1
return ret;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.