2011-05-15 2 views
4

가장 중요한 단어의 설정 비트 (예 : 0x00556844 -> 0x00156844)를 어떻게 해제 할 수 있습니까? gcc에 __builtin_clz이 있지만, 제로가 필요합니다. 이는 제게 불필요합니다. 또한, 어떻게 msvc 또는 인텔 C 컴파일러에 대한 __builtin_clz를 대체해야합니까?단어의 최상위 비트 설정 해제 (int32) [C]

현재 내 코드는

int msb = 1<< ((sizeof(int)*8)-__builtin_clz(input)-1); 
int result = input & ~msb; 

UPDATE입니다 :이 코드는 오히려 빠른 것을 말한다면 좋아, 나는이 코드에 이동성을 추가하는 방법, 당신을 요청할 것? 이 버전은 GCC 용이지만 MSVC & ICC?

+0

"단어의 가장 중요한 setted 비트"는 22 번째 비트입니까? 그게 내가 당신의 예제에서 볼 수있는 것 –

+0

아니, 그것은 주어진 int에서 설정되는 가장 중요한 비트입니다. 0x12345678의 결과는 0x02345678입니다. 0x0000-> 0x00000023 – osgx

+0

당신의 구현은 꽤 효율적입니다. 실제로 컴파일러가 빼기를 최적화하기 때문에 제 대답보다 훨씬 낫습니다. – hirschhornsalz

답변

7

가장 가까운 2의 제곱으로 반올림 한 다음 XOR하여 원래 값 (예 : Hacker's Delight에서 flp2()를 사용하여 :

uint32_t flp2(uint32_t x) // round x down to nearest power of 2 
{ 
    x = x | (x >> 1); 
    x = x | (x >> 2); 
    x = x | (x >> 4); 
    x = x | (x >> 8); 
    x = x | (x >>16); 
    return x - (x >> 1); 
} 

uint32_t clr_msb(uint32_t x) // clear most significant set bit in x 
{ 
    msb = flp2(x); // get MS set bit in x 
    return x^msb; // XOR MS set bit to clear it 
} 
+0

코드 속도와 내 속도를 비교할 수 있습니까? – osgx

+1

@osgx : 실행하고자하는 CPU에 달려 있습니다. 모든 CPU의 명령어 수가 0이나 그와 동등한 수는 아닙니다.그리고 물론 이식성 문제도 있습니다 ... –

+0

제 주 CPU는 x86 (Core2)이고 아마도 x86_64 (Core2)이며 clz 명령이 있습니다. 관심있는 이동성은 가장 일반적인 x86 및 x86_64 컴파일러 사이에서만 발생합니다. – osgx

1

당신은 MSVC를 들어

unsigned resetLeadingBit(x) { 
    return x & ~(0x80000000U >> __builtin_clz(x)) 
} 

을 할 수있는 31 -__ builtin_clz 인 _BitScanReverse있다().

실제로 BSR은 자연스러운 x86 명령어이며 gcc 내장 함수는 31-BSR로 구현됩니다.

5

성능에 진정으로 관심이있는 경우 msb를 지우는 가장 좋은 방법은 최근 BMI 지침을 추가하여 x86에서 변경되었습니다. 86 어셈블리에서

: 정상적으로 BMI 명령어를 지원하지 않는 비의 x86 아키텍처 이상 x86 프로세서를 위해 분해하면서 이제

clear_msb: 
    bsrq %rdi, %rax 
    bzhiq %rax, %rdi, %rax 
    retq 

는 C에 다시와 컴파일러 수 있도록하기 위해이 지침을 방출한다.

어셈블리 코드와 비교할 때 C 버전은 실제로보기 흉하고 자세한 정보가 표시됩니다. 그러나 최소한 이식성의 목표를 충족시킵니다. 그리고 필요한 하드웨어 및 컴파일러 지시문 (-mbmi, -mbmi2)이 있으면 컴파일 한 후에 멋진 어셈블리 코드로 돌아갑니다.

bsr()은 GCC/Clang 내장 명령에 의존합니다. 다른 컴파일러를 대상으로하는 경우 해당하는 이식 가능한 C 코드 및/또는 다른 컴파일러 관련 내장 명령으로 대체 할 수 있습니다.

#include <inttypes.h> 
#include <stdio.h> 

uint64_t bsr(const uint64_t n) 
{ 
     return 63 - (uint64_t)__builtin_clzll(n); 
} 

uint64_t bzhi(const uint64_t n, 
       const uint64_t index) 
{ 
     const uint64_t leading = (uint64_t)1 << index; 
     const uint64_t keep_bits = leading - 1; 
     return n & keep_bits; 
} 

uint64_t clear_msb(const uint64_t n) 
{ 
     return bzhi(n, bsr(n)); 
} 

int main(void) 
{ 
     uint64_t i; 
     for (i = 0; i < (uint64_t)1 << 16; ++i) { 
       printf("%" PRIu64 "\n", clear_msb(i)); 
     } 
     return 0; 
} 

어셈블리와 C 버전 모두 원래 질문이 제기되었으므로 자연스럽게 32 비트 명령어로 바뀝니다.

+0

Wiki에 따르면 BMI 확장은 Haswell https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets에서 가능합니다. 32 비트 모드 x86 및 64 비트 x86_64 모드에서 사용할 수 있습니까? bsrq/bzhiq 용 내장 함수가 있습니까? (최근의 Clang/gcc 이식성은 괜찮습니다.) – osgx

+1

@osgx 32 비트 명령어의 경우 'l'에 대한 어셈블리의 'q'접미어를 바꿀 수 있습니다. 또는 C 버전에서 uint64_t가 uint32_t로 바뀌면 어디에서나 볼 수 있습니다. bsr/bzhi가있는 내장 함수에 대해서는 GCC/Clang에 * intrinsics * ("immintrin.h"포함)가 있습니다.이 코드는 기본적으로 BMI 명령어를 사용할 수 있도록 해 주지만 코드가 정상적으로 성능이 떨어지지는 않지만 불법 명령어 BMI를 지원하지 않는 하드웨어. -mbmi -mbmi2를 추가하고 Haswell 또는 최신 CPU를 사용하면 실제로는 bsr/bzhi 만 컴파일됩니다. – user13972