(n & -n) == n이면 n이 2의 거듭 제곱 인 이유는 무엇입니까?


84

java.util.Random 소스의 라인 294에 따르면

if ((n & -n) == n) // i.e., n is a power of 2
    // rest of the code

왜 이런거야?


2
새 태그는 힌트 여야합니다. :)
bzlm 2011-09-13


2
비트를 따르십시오. 또한 2의 거듭 제곱으로 0을 계산합니다. 공식 (n & (n - 1)) == 0도 작동합니다 (남은 비트가 없으면 처음에 최대 1 비트가 설정되어있는 경우 최하위 비트를 제거합니다).
해롤드

3
네, 그러한 코드를 사용하는 것에 대해 유죄를 인정합니다. 2의 보수 산술을 처리하고 다양한 변환 및 오버플로 함정을 인식하고 있다는 것을 알고있는 한 이와 같은 여러 트릭을 재생할 수 있습니다. 추가 신용을 얻으려면 2의 다음 더 높은 거듭 제곱 또는 2-1의 거듭 제곱으로 반올림하는 방법을 알아 내십시오. 일부 분기에 놀라운 빈도로 수행해야하는 작업입니다.
Hot Licks 2011 년

1
잠깐, 요즘 모두 가 java.util.Random 소스를 읽고 있습니까? (나는 몇 달 전, 나는 그 이후 SO에 대한 몇 가지 질문을 기억 읽어 보시기 바랍니다.)
Mateen Ulhaq

답변:


48

(0 & -0) == 00은 2의 거듭 제곱이 아니기 때문에 설명이 완전히 정확 하지는 않습니다. 그것을 말하는 더 좋은 방법은

((n & -n) == n) n이 2의 거듭 제곱이거나 2의 제곱 또는 0의 음수 일 때.

n이 2의 거듭 제곱이면 이진수의 n은 단일 1 다음에 0이 오는 것입니다. 2의 보수에서 -n은 역 + 1이므로 비트가 정렬됩니다.

 n      0000100...000
-n      1111100...000
 n & -n 0000100...000

왜이 일이 일어나는지 알아보기 위해 2의 보수를 역 + 1로 간주하고 -n == ~n + 1

n          0000100...000
inverse n  1111011...111
                     + 1
two's comp 1111100...000

2의 보수를 얻기 위해 하나를 추가 할 때 하나를 끝까지 가지고 있기 때문입니다.

n이 2의 거듭 제곱이 아닌 경우 † 2의 보수가 해당 캐리로 인해 가장 높은 비트 세트를 갖지 않기 때문에 결과는 비트가 누락됩니다.

†-또는 2의 제곱의 0 또는 음수 ... 위에 설명 된대로.


그리고 최하위 1 비트를 분리하는 트릭이 있습니다.
Hot Licks 2011 년

2
에 관해서는 (0 & -0) == 0, 바로 선행 문 이다 if (n <= 0) throw .... 테스트중인 숫자는 해당 시점에서 절대 0 (또는 음수)이 될 수 없음을 의미합니다.
사용자

1
@Michael, 맞아요. 나는 내가 Random.java읽지 않은 것을 비판하지 않고 제목의 질문에 대답하고 있었다 .
Mike Samuel

1
@ Mike, 나는 그것을 깨닫는다; 그러나 코드 (질문에 포함되어 있고 제목의 질문에 대한 기초가 됨)의 주석에있는 진술이 바로 이전에 설정된 전제 조건의 맥락에서 볼 수 없을 때 그 자체로 존재한다고 생각하지 않습니다. 코드에서. 여기에 게시 된 질문 보면 유형 n이 무엇인지 알 수 없습니다 . 나는이 가정을 확인하지 않았지만 어떻게 든 a double가 같은 방식으로 행동 할 것이라고 의심한다 .
사용자

3
@Michael, n이 질문에는 "java"태그가 있기 때문에 유형에 대해 꽤 좋은 경계를 둘 수 있습니다 . &에 정의되지 않은 double또는 floatJava로. 정수 유형 및 부울에만 정의됩니다. -부울에 대해 정의되지 않았기 때문에 우리는 안전하게 n적분을 추론 할 수 있습니다 .
Mike Samuel

95

때문에 2의 보수에 -n있다 ~n+1.

n가 2의 거듭 제곱 이면 1 비트 만 설정됩니다. 그래서 ~n그 비트를 제외한 모든 비트가 설정되었습니다. 하나를 추가하면 그 보장, 다시 특별 비트를 설정 n & (that thing)같다 n.

그 반대는 또한 해당 Java 소스의 이전 줄에서 0과 음수가 제외 되었기 때문에 사실입니다. 경우 n다음 하나 개 이상의 비트 세트를 가지고 그 중 하나는 최고 이러한 비트입니다. 이 비트는 "흡수"할 더 낮은 클리어 비트가 있으므로에 의해 설정 되지 않습니다+1 .

 n: 00001001000
~n: 11110110111
-n: 11110111000  // the first 0 bit "absorbed" the +1
        ^
        |
        (n & -n) fails to equal n at this bit.

13

이것이 사실 인 이유를 확인하려면 값을 비트 맵으로보아야합니다.

1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

따라서 두 필드가 모두 1 인 경우에만 1이 나옵니다.

이제 -n은 2의 보수를합니다. 이 모든 변화 01그리고 1을 추가합니다.

7 = 00000111
-1 = NEG(7) + 1 = 11111000 + 1 = 11111001

하나

8 = 00001000
-8 = 11110111 + 1 = 11111000 

00001000  (8)
11111000  (-8)
--------- &
00001000 = 8.

2의 거듭 제곱에 대해서만 (n & -n)n이됩니다.
이것은 2의 거듭 제곱이 0의 긴 바다에서 단일 세트 비트로 표현되기 때문입니다. 부정은 1의 바다에서 정반대의 단일 0 (1이 있었던 지점) 을 산출합니다 . 1을 더하면 낮은 값이 0이있는 공간으로 이동합니다.
그리고 비트 및 (&)는 1을 다시 필터링합니다.


8

2의 보수 표현에서 2의 거듭 제곱에 대한 독특한 점은 n = 2 ^ k 인 k 번째 비트를 제외하고 모두 0 비트로 구성된다는 것입니다.

base 2    base 10
000001 =  1 
000010 =  2
000100 =  4
     ...

2의 보수에서 음수 값을 얻으려면 모든 비트를 뒤집고 1을 더합니다. 2의 거듭 제곱의 경우, 이는 양수 값에있는 1 비트까지 포함하여 왼쪽에 1 묶음과 오른쪽에 0 묶음이 있음을 의미합니다.

n   base 2  ~n      ~n+1 (-n)   n&-n  
1   000001  111110  111111      000001
2   000010  111101  111110      000010
4   000100  111011  111100      000100
8   001000  110111  111000      001000

2 열과 4 열의 결과가 2 열과 동일하다는 것을 쉽게 알 수 있습니다.

이 차트에서 누락 된 다른 값을 살펴보면 이것이 2의 거듭 제곱 외에는 어떤 것도 유지하지 않는 이유를 알 수 있습니다.

n   base 2  ~n      ~n+1 (-n)   n&-n  
1   000001  111110  111111      000001
2   000010  111101  111110      000010
3   000011  111100  111101      000001
4   000100  111011  111100      000100
5   000101  111010  111011      000001
6   000110  111001  111010      000010
7   000111  111000  111001      000001
8   001000  110111  111000      001000

n & -n은 (n> 0 인 경우) 1 비트 만 설정되며 해당 비트는 n에서 최하위 설정 비트가됩니다. 2의 거듭 제곱 인 모든 숫자의 경우 최하위 세트 비트가 유일한 세트 비트입니다. 다른 모든 숫자에는 둘 이상의 비트 세트가 있으며 그 중 최하위 비트 만 결과에 설정됩니다.


4

2의 거듭 제곱과 2 의 보수 의 속성입니다 .

예를 들어, 8을 사용하십시오.

8  = 0b00001000

-8 = 0b11111000

2의 보수 계산 :

Starting:  0b00001000
Flip bits: 0b11110111  (one's complement)
Add one:   0b11111000  

AND 8    : 0b00001000

2의 멱수를 들어, 단지 하나의 비트가 설정 될 것이다 첨가하면 N 발생할 수 있도록 2의 비트를 N 으로 설정되어야한다 (하나의 N에 들고 유지 번째 비트). 그런 다음 AND두 개의 숫자가 있으면 원래의 숫자를 얻습니다.

2의 거듭 제곱이 아닌 숫자의 경우 다른 비트가 뒤집 히지 않으므로 AND원래 숫자가 생성되지 않습니다.


4

간단히 말해서, n이 2의 거듭 제곱이면 한 비트 만 1로 설정되고 나머지 비트는 0으로 설정됩니다.

00000...00001 = 2 ^ 0
00000...00010 = 2 ^ 1
00000...00100 = 2 ^ 2
00000...01000 = 2 ^ 3
00000...10000 = 2 ^ 4

and so on ...

그리고 -n2의 보수 이기 때문에 n(즉, 1 인 유일한 비트는 그대로 남아 있고 해당 비트의 왼쪽에있는 비트는 1에 위치합니다. 이는 AND 연산자의 결과 &가 0이 되므로 실제로 중요하지 않습니다. 두 비트 중 하나가 0 임) :

000000...000010000...00000 <<< n
&
111111...111110000...00000 <<< -n
--------------------------
000000...000010000...00000 <<< n

0

예를 통해 표시 :

16 진수 8 = 0x000008

-8 in hex = 0xFFFFF8

8 & -8 = 0x000008

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