C ++에서 숫자가 2의 거듭 제곱인지 테스트하는 가장 간단한 방법은 무엇입니까?


94

다음과 같은 기능이 필요합니다.

// return true iff 'n' is a power of 2, e.g.
// is_power_of_2(16) => true  is_power_of_2(3) => false
bool is_power_of_2(int n);

누구든지 내가 이것을 어떻게 쓸 수 있는지 제안 할 수 있습니까? 이런 종류의 알고리즘을 찾을 수있는 좋은 웹 사이트를 알려주시겠습니까?



@rootTraveller-아마도 중복이 아닙니다. C ++와 Java는 서로 다른 언어이며 각각 다른 기능을 제공합니다. 예를 들어, C / C ++에서 이제 BMI 지원 프로세서와 함께 내장 함수를 사용할 수 있습니다.이 프로세서는 한 번의 클럭으로 수행하도록 기계 명령을 발행합니다. Java에는 수학 루틴에서 나온 것과 같은 다른 것이 있다고 생각합니다.
jww

답변:


191

(n & (n - 1)) == 0최고. 그러나 n = 0에 대해 부정확하게 true를 반환하므로 가능하면 명시 적으로 확인하는 것이 좋습니다.

http://www.graphics.stanford.edu/~seander/bithacks.html 에는 이것을 포함하여 영리한 비트 트위들 링 알고리즘이 많이 있습니다.


8
그래서 기본적으로(n>0 && ((n & (n-1)) == 0))
Saurabh 고얄

1
@SaurabhGoyal 또는 n && !(n & (n - 1))답변 내 링크로 표시됩니다.
Carsten

왜, 오 왜, 이것이 대답의 상단에 있지 않습니까? OP 수락하십시오.
donturner

@SaurabhGoyal 한 가지 작은 개선점은 이것이다 : n & !(n & (n - 1)). 비트 AND &(논리적 and &&아님)에 주목하십시오 . 비트 연산자는 단락을 구현하지 않으므로 코드가 분기되지 않습니다. 이는 분기 오 예측 가능성이 있고 표현식 (즉, !(n & (n - 1))) 의 rhs를 계산하는 것이 저렴한 상황에서 바람직 합니다.
Cassio Neri

@cassio !는 논리 연산자이므로의 값은 !(n & (n - 1))부울이됩니다. 비트 AND 연산자에 부울과 숫자를 지정할 수 있습니까? 그렇다면 괜찮아 보입니다.
Saurabh Goyal

81

2의 거듭 제곱은 하나의 비트 세트 (부호없는 숫자의 경우) 만 갖습니다. 같은 것

bool powerOfTwo = !(x == 0) && !(x & (x - 1));

잘 작동합니다. 2의 거듭 제곱보다 작은 것은 하위 비트에서 모두 1이므로 AND는 비트 단위로 0이어야합니다.

서명되지 않은 숫자를 가정하고 있었기 때문에 == 0 테스트 (원래 잊고 있었지만 죄송합니다)가 적절합니다. 부호있는 정수를 사용하는 경우> 0 테스트를 원할 수 있습니다.


'!'가 누락되었습니다. 또는 '== 0'

또한 x의 음수 값에 대한 테스트가 누락되었습니다.
Rob Wells

깔끔하게, 'x 분 전에 수정 됨'이 표시되지 않고 어떻게 수정 했나요?

진지하게, 명백하게 잘못된 대답에 대해 어떻게 120 회를 얻었습니까?

@Mike F : 사실 사람들이 답을 확인하지 않고 투표 할 것 같습니다. 누구든지 실수를 할 수 있습니다. 나중에 어떤 일이 생기면 자유롭게 수정할 수 있습니다.
Adam Wright

49

이진법에서 2의 거듭 제곱은 다음과 같습니다.

1: 0001
2: 0010
4: 0100
8: 1000

항상 정확히 1 비트 세트가 있습니다. 유일한 예외는 부호있는 정수입니다. 예를 들어 값이 -128 인 부호있는 8 비트 정수는 다음과 같습니다.

10000000

따라서 숫자가 0보다 큰지 확인한 후 영리한 해킹을 사용하여 1 비트 만 설정되었는지 테스트 할 수 있습니다.

bool is_power_of_2(int x) {
    return x > 0 && !(x & (x−1));
}

좀 더 자세히 알아 보려면 여기를 참조 하세요 .


14

접근법 # 1 :

그것을 확인하기 위해 은밀하게 숫자를 2로 나눕니다.

시간 복잡도 : O (log2n).

접근법 # 2 :

비트 AND 바로 이전 숫자가있는 숫자는 0과 같아야합니다.

예 : Number = 8 Binary of 8 : 1 0 0 0 Binary of 7 : 0 1 1 1이고 두 숫자의 비트 AND는 0 0 0 0 = 0입니다.

시간 복잡도 : O (1).

접근법 # 3 :

비트 단위 XOR 이전 숫자가있는 숫자는 두 숫자의 합이어야합니다.

예 : Number = 8 Binary of 8 : 1 0 0 0 Binary of 7:01 1 1이고 두 숫자의 비트 단위 XOR은 1 1 1 1 = 15입니다.

시간 복잡도 : O (1).

http://javaexplorer03.blogspot.in/2016/01/how-to-check-number-is-power-of-two.html


8
bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}

7

2의 거듭 제곱에 대해 다음도 적용됩니다.

n & (-n) == n

참고 : 조건은 2의 거듭 제곱이 아니지만 n = 0에 대해 참입니다.
이것이 작동하는 이유는 다음과 같습니다.
-n은 n의 2s 보수입니다. -n은 n에 비해 가장 오른쪽에 설정된 비트의 왼쪽에있는 모든 비트를 뒤집습니다. 2의 거듭 제곱의 경우 한 세트 비트 만 있습니다.


2
나는 조건이 N 마찬가지입니다 의미 = 0하지만이 자사하지 파워
동결 프란시스

n이 서명되지 않은 경우 발생하는 변환과 함께 작동합니까?
Joseph Garvin

6

GCC를 사용하는 경우 이것이 아마도 가장 빠를 것입니다. POPCNT cpu 명령어와 하나의 비교 만 사용합니다. 2의 거듭 제곱에 대한 이진 표현은 항상 하나의 비트 만 설정하고 다른 비트는 항상 0입니다. 그래서 우리는 POPCNT로 설정된 비트의 수를 세고 만약 1과 같으면 그 수는 2의 거듭 제곱입니다. 가능한 더 빠른 방법은 없다고 생각합니다. 한 번 이해했다면 매우 간단합니다.

if(1==__builtin_popcount(n))

아니. 방금 테스트했습니다. 나는 popcount를 좋아하지만 2의 거듭 제곱 테스트의 i && !(i & (i - 1)))경우 gcc에서 네이티브 어셈블리 POPCNT 명령을 활성화 할 때에도 내 컴퓨터 에서 테스트 가 약 10 % 더 빠릅니다.
eraoul

죄송합니다. 내 테스트 프로그램이 루프에서 실행되고 분기 예측이 "속임수"였습니다. CPU에 POPCNT 명령이 있으면 더 빠릅니다.
eraoul

5

C ++ 20에는 std::ispow2직접 구현할 필요가없는 경우 정확히 이러한 목적으로 사용할 수있는 것이 있습니다.

#include <bit>
static_assert(std::ispow2(16));
static_assert(!std::ispow2(15));

3

다음은 부울 단락과 비교가 느리다는 사실로 인해 대부분의 찬성 응답보다 빠를 것입니다.

int isPowerOfTwo(unsigned int x)
{
  return x && !(x & (x – 1));
}

x가 0이 될 수 없다는 것을 알고 있다면

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x – 1));
}


3

C ++에서 숫자가 2의 거듭 제곱인지 테스트하는 가장 간단한 방법은 무엇입니까?

Bit Manipulation Instructions 가있는 최신 Intel 프로세서가있는 경우 다음을 수행 할 수 있습니다. 다른 사람들이 이미 대답했기 때문에 곧은 C / C ++ 코드를 생략하지만 BMI를 사용할 수 없거나 활성화되지 않은 경우 필요합니다.

bool IsPowerOf2_32(uint32_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u32(x));
#endif
    // Fallback to C/C++ code
}

bool IsPowerOf2_64(uint64_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u64(x));
#endif
    // Fallback to C/C++ code
}

GCC, ICC 및 Clang 신호 BMI 지원 __BMI__. AVX2를 사용할 수 있고 활성화 된 경우 Visual Studio 2015 이상의 Microsoft 컴파일러에서 사용할 수 있습니다. 필요한 헤더는 SIMD 내장 함수의 헤더 파일을 참조하세요 .

나는 일반적 으로 i686에서 컴파일하는 경우를 _blsr_u64사용하여 보호합니다 _LP64_. Clang은 약간 다른 고유 기호 이름을 사용하기 때문에 약간의 해결 방법이 필요합니다.

#if defined(__GNUC__) && defined(__BMI__)
# if defined(__clang__)
#  ifndef _tzcnt_u32
#   define _tzcnt_u32(x) __tzcnt_u32(x)
#  endif
#  ifndef _blsr_u32
#    define  _blsr_u32(x)  __blsr_u32(x)
#  endif
#  ifdef __x86_64__
#   ifndef _tzcnt_u64
#    define _tzcnt_u64(x) __tzcnt_u64(x)
#   endif
#   ifndef _blsr_u64
#     define  _blsr_u64(x)  __blsr_u64(x)
#   endif
#  endif  // x86_64
# endif  // Clang
#endif  // GNUC and BMI

이런 종류의 알고리즘을 찾을 수있는 좋은 웹 사이트를 알려주시겠습니까?

이 웹 사이트는 종종 인용됩니다 : Bit Twiddling Hacks .


이것은 확실히 OP에서 요청한 "가장 간단한 방법"은 아니지만 특정 환경에서 가장 빠른 방법입니다. 다양한 아키텍처에 대해 조건화하는 방법을 보여주는 것은 매우 유용합니다.
fearless_fool

1

이것은 가장 빠르거나 짧은 방법은 아니지만 매우 읽기 쉽다고 생각합니다. 그래서 나는 다음과 같이 할 것입니다.

bool is_power_of_2(int n)
  int bitCounter=0;
  while(n) {
    if ((n & 1) == 1) {
      ++bitCounter;
    }
    n >>= 1;
  }
  return (bitCounter == 1);
}

바이너리는 2의 거듭 제곱을 기반으로하기 때문에 작동합니다. 비트 세트가 하나 뿐인 숫자는 2의 거듭 제곱이어야합니다.


빠르거나 짧지 않을 수 있지만 상위 답변과 달리 정확합니다.

2
댓글을 달았을 때 그들은 모두 도청되었습니다. 이후 허용 가능한 상태로 편집되었습니다.

0

다음은 |대신 사용 하는 다른 방법 입니다 &.

bool is_power_of_2(int x) {
    return x > 0 && (x<<1 == (x|(x-1)) +1));
}

0

C ++를 통해 가능합니다.

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}

2
그것은 나에게 간단하지도 빠르지도 않습니다.
luk32

2
log2, 으로 인해 확실히 빠르지 않으며 작동한다는 증거는 설명하기가 쉽지 않습니다 (정확히 반올림 오류로 인해 잡힐 수 있습니까?). 또한 불필요하게 if..return..else..return. 접는 것이 잘못된 이유return x==(double)y; ? boolanyayws 를 반환해야합니다 . IMO는 정말로 고수하고 싶다면 삼항 연산자조차 더 명확 할 것 int입니다.
luk32

0

나는 이것이 매우 오래된 게시물 이라는 것을 알고 있지만 이것을 여기에 게시하는 것이 흥미로울 것이라고 생각했습니다.


에서 코드 골프 SE (그래서이 쓴 사람 (들)에 대한 모든 신용) : 언어의 쇼케이스를

( C 에 대한 단락 에 , 하위 단락 길이 36 스 니펫 )

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}

-1

가장 빠르지는 않을 수도있는 또 다른 방법은 ln (x) / ln (2)가 정수인지 확인하는 것입니다.


2
그것에 대해 아마 없습니다 :-).
paxdiablo

1
이것은 부동 소수점 부정확성에 문제가 있습니다. ln (1 << 29) / ln (2)는 29.000000000000004가됩니다.
Anonymous

-3

다음은 T-SQL (SQL Server)의 비트 시프트 방법입니다.

SELECT CASE WHEN @X>0 AND (@X) & (@X-1)=0 THEN 1 ELSE 0 END AS IsPowerOfTwo

로그를 4 번 수행하는 것보다 훨씬 빠릅니다 (첫 번째는 10 진수 결과를 얻고 두 번째는 정수를 설정하고 비교).


5
이 질문에 대한 상위 답변이 T-SQL에서도 구현 될 수있는 방법을 확인하는 것은 좋지만 여기에서 묻는 질문과 실제로 관련이 없습니다. 대안 (T-SQL에서 솔루션을 찾고 있고이 질문에 대한 답변을 찾았고 T-SQL로 구현했으며이 답변을 게시하는 것이 충분히 흥미 로웠다고 생각하는 경우)는 T-SQL을 참조하여 질문을 게시하는 것입니다. 이 답변 된 질문을 참조하여 직접 답변하십시오. 이 제안이 도움이되기를 바랍니다.
Simon

이것은 정말이 질문에 대답하지 않는다
phuclv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.