정수를 2로 나누는 데 더 나은 옵션은 무엇입니까?


406

정수를 2로 나누기에 가장 적합한 옵션은 다음 중 어느 것입니까?

기술 1 :

x = x >> 1;

기술 2 :

x = x / 2;

여기 x정수가 있습니다.


75
실제로 결과를 x다시 할당하려면 다음 과 같은 방법으로도 적절하지 않습니다 . 연산으로 표현하려는 대상에 따라 x >>= 1또는 중 하나 여야합니다 x /= 2. 더 빠르기 때문에 (현대 컴파일러는 어쨌든 동일한 변형을 모두 동일하고 빠른 어셈블리로 컴파일합니다) 혼동이 적기 때문입니다.
leftaroundabout

33
나는 좌회전에 동의하지 않습니다. -그러나 많은 프로그래밍 언어에는 부호 비트를 유지하고 부호있는 값에 대해 예상대로 작동하는 산술 시프트 라는 연산이 있습니다. 구문은 다음과 같습니다 x = x >>> 1. 또한 플랫폼과 컴파일러에 따라 시프트를 사용하여 나눗셈과 곱셈을 수동으로 최적화하는 것이 상당히 합리적 일 수 있습니다. -곱셈에 대한 직접적인 ALU 지원없이 마이크로 컨트롤러를 생각합니다.
JimmyB

36
monadic bind와 너무 비슷해 보이기 x /= 2때문에 선호합니다 x >>= 1.)
fredoverflow

19
@leftaroundabout - 난 그냥 더 많은 읽기 쓰기하다고 생각하는 x = x / 2대신 x /= 2. 주관적인 선호 :)
JimmyB

8
@ HannoBinder : 특히 주관적, 특히 많은 습관. 모든 산술 연산자의 ⬜=조합 이있는 언어의 IMO 는 가능할 때마다 사용해야합니다. 그것은 사실에 소음과 풋 강조 제거 x되어 수정을 일반 동안, =운전자가 오히려 예전의 완전히 새로운 가치 독립적 인 취 제안합니다. - 항상뿐만 아니라 그 지점이있을 수 있습니다,하지만 당신은 매우 유용을 포기해야 할 것 (단 수학 연산자를 아는 사람은 그래서 읽을 수 있어요 그래서) 결합 연산자를 피하고 ++, --, +=, 너무.
leftaroundabout

답변:


847

수행하려는 작업을 가장 잘 설명하는 작업을 사용하십시오.

  • 숫자를 일련의 비트로 취급하는 경우 비트 시프트를 사용하십시오.
  • 수치로 취급하는 경우 나누기를 사용하십시오.

그것들은 정확히 동일하지 않습니다. 음수에 대해 다른 결과를 줄 수 있습니다. 예를 들면 다음과 같습니다.

-5 / 2  = -2
-5 >> 1 = -3

(아이디어)


20
원래 질문은 또한 '최고'라는 용어에 대해 모호했습니다. 속도, 가독성, 학생들을 속이는 시험 문제 등에서 '최고'... '최고'가 무엇을 의미하는지에 대한 설명이 없으면 이것이 가장 정답 인 것 같습니다.
Ray

47
C ++ 03에서 둘 다 음수로 정의 된 구현이며 동일한 결과를 제공 할 수 있습니다 . C ++ 11에서 나누기는 음수에 대해 잘 정의되어 있지만 시프트는 여전히 구현 정의입니다.
James Kanze

2
/의 정의는 초기 C 표준에 정의 된 구현 (음수의 경우 반올림 또는 내림)입니다. 항상 % (모듈러스 / 나머지 연산자)와 일치해야합니다.
ctrl-alt-delor

7
"구현 정의"는 컴파일러의 구현자가 일반적으로 상당한 제약 조건을 가진 여러 구현 선택 중에서 선택해야 함을 의미합니다. 여기서 한 가지 제약 조건은 %/연산자가 양수 및 음수 피연산자 모두에 대해 일관성이 있어야하므로 및 (a/b)*b+(a%b)==a의 부호에 관계없이 적용 됩니다. 일반적으로 저자는 CPU에서 최상의 성능을 얻을 수있는 선택을합니다. ab
RBerteig

6
"컴파일러가 어쨌든 그것을 변환으로 변환 할 것"이라고 말하는 사람들은 모두 틀렸습니까? 컴파일러가 음이 아닌 정수를 처리한다고 보증하지 않는 한 (상수이거나 부호없는 정수임), 시프트로 변경할 수 없습니다.
Kip

225

첫 번째는 나누는 것처럼 보입니까? 아니요 x / 2. 나누려면을 사용하십시오 . 컴파일러는 가능하면 비트 시프트를 사용하도록 최적화 할 수 있으며 (강제 감소라고 함) 직접 수행하는 경우 쓸모없는 미세 최적화가 가능합니다.


15
많은 컴파일러는 2의 제곱으로 비트 시프트로 나누지 않습니다. 부호있는 정수에 대한 잘못된 최적화입니다. 컴파일러의 어셈블리 출력을보고 직접 확인해야합니다.
exDM69

1
IIRC는 CUDA에서 병렬 감소를 더 빠르게하기 위해 사용했습니다 (정수 div 피하십시오). 그러나 이것은 1 년 전 이었지만 요즘 CUDA 컴파일러가 얼마나 똑똑한 지 궁금합니다.
Nils

9
@ exDM69 : 많은 컴파일러는 부호있는 정수조차도 그렇게하고 부호에 따라 조정합니다. 이러한 것들로 재생하는 좋은 도구는 이것이다 tinyurl.com/6uww253
PlasmaHH

19
@ exDM69 : 그리고 관련이 있습니다. 어떻게? "항상"이 아니라 "가능한 경우"라고 말했습니다. 최적화가 정확하지 않은 경우 수동으로 수행해도 정확하지 않습니다 (또한 언급했듯이 GCC는 부호있는 정수의 적절한 대체를 알아낼 정도로 똑똑합니다).
Cat Plus Plus

4
WikiPedia 페이지를 보면 논란의 여지가 있지만 강도 감소라고는 생각하지 않습니다. 루프에서 강도 감소는 예를 들어 루프의 이전 값에 더함으로써 곱셈에서 덧셈으로 줄일 때입니다. 이것은 컴파일러가 상당히 안정적으로 수행 할 수있는 들여다 보는 구멍 최적화에 가깝습니다.
SomeCallMeTim

189

쌓아 올리려면 x = x / 2; : 사용하는 것을 선호하는 많은 이유 가 있습니다.

  • 그것은 당신의 의도를보다 명확하게 표현합니다 (레지스터 비트 또는 비트를 다루지 않는다고 가정하면)

  • 컴파일러는 어쨌든 이것을 시프트 연산으로 줄입니다.

  • 컴파일러가 축소하지 않고 시프트보다 느린 연산을 선택하더라도 측정 가능한 방식으로 프로그램의 성능에 영향을 줄 가능성은 그 자체로 거의 작지 않습니다 (측정에 영향을 미치는 경우 실제 교대 사용 이유)

  • 나누기가 더 큰 표현식의 일부인 경우 나누기 연산자를 사용하면 우선 순위가 더 높아집니다.

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
  • 부호있는 산술은 위에서 언급 한 우선 순위 문제보다 훨씬 복잡 할 수 있습니다.

  • 다시 말하지만 컴파일러는 이미이 작업을 수행합니다. 실제로, 그것은 2의 거듭 제곱이 아니라 모든 종류의 숫자에 대해 상수로 나누기를 일련의 시프트, 덧셈 및 곱셈으로 변환합니다. 이에 대한 자세한 정보 링크는 이 질문 을 참조하십시오 .

간단히 말해서, 버그를 도입 할 가능성이 높은 것을 제외하고는 실제로 곱셈 또는 나눗셈을 할 때 시프트를 코딩하여 아무것도 사지 않습니다. 컴파일러가 이러한 종류의 것을 적절하게 전환하도록 최적화 할만큼 똑똑하지 못했기 때문에 평생입니다.


5
우선 순위 규칙이 있지만 괄호를 사용하는 데있어 잘못된 것은 없다는 점을 덧붙일 가치가 있습니다. 일부 프로덕션 코드를 개정하는 동안 실제로 훨씬 더 읽기 쉬운 대신 형식 변수 a/b/c*d( a..d숫자 변수로 표시)를 보았습니다 (a*d)/(b*c).

1
성능 및 최적화는 컴파일러와 대상에 따라 다릅니다. 예를 들어 상용 컴파일러를 구입하지 않으면 -O0보다 높은 것이 비활성화 된 마이크로 컨트롤러에서 일부 작업을 수행하므로 컴파일러는 비트 시프트로 나눌 수 없습니다. 또한 비트 시프트는 한 사이클을 필요로하고 나누기에는 18 사이클이 걸리며, 마이크로 컨트롤러의 클럭 속도는 매우 느리기 때문에 실제로 눈에 띄는 성능 저하 일 수 있습니다 (그러나 코드에 따라 다릅니다. 그것의 문제!)

4
@ JackManey, 오버플로가 발생할 가능성이 a*d있거나 b*c읽을 수없는 형식은 동일하지 않으며 명백한 이점이 있습니다. 추신 : 나는 괄호가 가장 친한 친구라는 데 동의합니다.
Mark Ransom

@MarkRansom-공정 포인트 ( a/b/c*dR 코드에서 실행되었지만 오버플로가 데이터에 심각한 문제가 있음을 의미하는 상황에서 성능에 중요한 C 코드 블록이 아니라는 맥락에서).

이 코드 x=x/2;는 홀수 음수가 아니거나 한 번에 하나씩 오류를 신경 쓰지 않는 x>>=1경우 보다 "명확합니다" x. 그렇지 않으면 x=x/2;x>>=1;다른 의미를 가지고있다. 에 의해 계산 된 값이 필요한 것이면 나 x>>=1보다 더 명확한 것으로 간주 하거나 다른 공식을 2로 나누는 것으로 생각할 수 있습니다. 마찬가지로에 의해 계산 된 값이 필요한 경우 보다 명확합니다 . x = (x & ~1)/2x = (x < 0) ? (x-1)/2 : x/2x/=2((x + ((unsigned)x>>31)>>1)
supercat

62

가장 좋은 방법은 무엇이며 정수를 2로 나누는 이유는 무엇입니까?

최선의 의미에 따라 다릅니다 .

동료가 당신을 미워하거나 코드를 읽기 어렵게하려면 첫 번째 옵션을 사용하십시오.

숫자를 2로 나누려면 두 번째 숫자로 이동하십시오.

숫자가 음수이거나 더 큰 표현식 안에 있으면 둘이 동일하지 않습니다. 비트 시프트가 우선 순위가 낮 +거나 -나눗셈이 우선 순위가 높습니다.

의도를 나타내는 코드를 작성해야합니다. 성능이 염려 되더라도 걱정하지 마십시오. 옵티마이 저는 이러한 종류의 마이크로 최적화에서 훌륭하게 작동합니다.


58

/더 명확하다고 가정하고 나누기 ( )를 사용하십시오 . 컴파일러는 그에 따라 최적화됩니다.


34
컴파일러 그에 따라 최적화 해야합니다 .
녹 티스 스카이 타워

12
컴파일러가없는 최적화 따라 않는 경우, 당신은 해야 더 나은 컴파일러를 사용합니다.
David Stone

3
@DavidStone :에 어떤 프로세서 (1) 이외의 다른 일정으로 가능성이 음성 부호있는 정수의 컴파일러 최적화 부문의 변화 효율적으로 할 수?
supercat

1
@ supercat : 좋은 지적입니다. 물론 부호없는 정수에 값을 저장할 수 있습니다 (서명 된 / 부호없는 불일치 경고와 결합 할 때보 다 평판이 훨씬 좋지 않습니다). 대부분의 컴파일러는 최적화 할 때 무언가를 참이라고 가정하는 방법을 가지고 있습니다 . 호환성 매크로로 랩핑하고 ASSUME(x >= 0); x /= 2;over 과 같은 것을 선호 x >>= 1;하지만 여전히 중요한 점입니다.
David Stone

39

x / 2의도가 명확하고 컴파일러가 최적화하기 때문에 선호 해야하는 다른 답변에 동의 합니다.

그러나 선호하는 또 다른 이유 x / 2이상이 x >> 1의 행동이 있다는 것입니다 >>경우 구현에 의존 x서명입니다 int및 부정적이다.

6.5.7 섹션, ISO C99 표준의 글 머리표 5에서 :

결과 E1 >> E2E1오른쪽으로 이동 된 E2비트 위치입니다. 경우 E1부호 타입을 갖는 경우, 또는 E1서명 유형 및 음이 아닌 값을 가지며, 그 결과의 값의 몫의 필수적인 부분이다 E1/ 2 E2. 경우 E1서명 유형 및 음의 값을 가지며, 그 결과 값은 구현 정의된다.


3
x>>scalepower음수에 대해 많은 구현에서 정의하는 동작 은 화면 렌더링과 같은 목적으로 값을 2의 거듭 제곱으로 나눌 때 필요한 것입니다. 반면 x/scalefactor에 음수 값에 수정을 적용하지 않으면 사용 이 잘못됩니다.
supercat

32

x / 2명확하고, x >> 1(약 30 % 빠른 자바 JVM를 들어, 마이크로 벤치 마크에 따라) 훨씬 더 빨리 없습니다. 다른 사람들이 지적했듯이 음수의 경우 반올림이 약간 다르므로 음수를 처리하려면이를 고려해야합니다. 일부 컴파일러는 숫자가 음수가 될 수 없다는 것을 알면 자동으로 변환 x / 2x >> 1수 있습니다 (심지어 내가 이것을 확인할 수 없다고 생각하더라도).

심지어 x / 2때문에 (느린) 부문의 CPU 명령어를 사용할 수 없습니다 일부 바로 가기가 가능 하지만, 여전히 느린보다 x >> 1.

(이 질문, 다른 프로그래밍 언어보다 연산자를 가지고 ++는 C / C입니다. 자바의 경우 부호없는 오른쪽 시프트도 있습니다, x >>> 1다시 다른있다.이 때문에, 정확하게 두 값의 평균 (평균) 값을 계산 할 수 있습니다 (a + b) >>> 1의지 a및의 값이 매우 큰 경우에도 평균값을 반환합니다 b. 예를 들어, 배열 인덱스가 매우 커질 수있는 경우 이진 검색에 필요합니다. 이진 검색의 여러 버전(a + b) / 2 에는 평균을 계산하는 데 사용 된 버그 가있었습니다 . 제대로 작동 (a + b) >>> 1하지 않습니다. 대신 올바른 해결책을 사용하는 것입니다.)


1
컴파일러는 변환 할 수 없습니다 x/2x>>1경우에 x음수가 될 수 있습니다. 원하는 x>>1것이 계산할 값 이라면 x/2, 같은 값을 계산하는 식보다 거의 빠를 것 입니다.
supercat

네 말이 맞아 컴파일러는 값이 음수가 아닌 것을 알고있는 경우 에만 변환 x/2할 수 있습니다 x>>1. 답변을 업데이트하려고합니다.
토마스 뮬러

컴파일러는 여전히 방지 할 div변환함으로써, 비록 명령 x/2으로 (x + (x<0?1:0)) >> 1(단, >> 그 부호 비트 시프트 연산 오른쪽 시프트이다). 여기에는 4 개의 명령이 필요합니다. 값 shr (reg에서 부호 비트 만 가져 오기), 추가, sar. goo.gl/4F8Ms4
Peter Cordes

질문은 C와 C ++로 태그됩니다.
조쉬 샌포드

22

크 누스는 말했다 :

조기 최적화는 모든 악의 근원입니다.

그래서 나는 사용하는 것이 좋습니다 x /= 2;

이렇게하면 코드를 이해하기 쉽고이 형식 으로이 작업을 최적화한다고 프로세서에 큰 차이가 있다고 생각하지 않습니다.


4
정수가 공리 (자연수와 실수에 적용됨)를 유지하기를 원한다면 (n + d) / d = (n / d) + 1? 그래픽 스케일링시 공리를 위반하면 결과에 "이음새"가 표시됩니다. 균일하고 거의 0에 대해 대칭 인 것을 원한다면 (n+8)>>4잘 작동합니다. 서명 된 오른쪽 시프트를 사용하지 않고 명확하거나 효율적인 접근 방식을 제공 할 수 있습니까?
supercat

19

결정하는 데 도움이되는 컴파일러 출력을 살펴보십시오.
gcc (GCC) 4.2.1 20070719를 사용 하여 x86-64에서이 테스트를 실행했습니다. [FreeBSD]

또한 godbolt에서 온라인으로 컴파일러 출력을 참조하십시오 .

컴파일러가 sarl두 경우 모두 (산술 오른쪽 이동) 명령어를 사용하므로 두 표현식 간의 유사성을 인식합니다. 나누기를 사용하는 경우 컴파일러도 음수를 조정해야합니다. 이를 위해 부호 비트를 최하위 비트로 이동시키고 결과에 추가합니다. 이것은 나누기가하는 것과 비교하여 음수를 바꿀 때 발생하는 문제를 해결합니다.
나누기 케이스는 2 개의 시프트를 수행하지만 명시적인 시프트 케이스는 1 개의 시프트 만 수행하므로 여기서는 다른 답변으로 측정 한 성능 차이를 설명 할 수 있습니다.

어셈블리 출력을 가진 C 코드 :

나누기의 입력은

int div2signed(int a) {
  return a / 2;
}

이 컴파일

    movl    %edi, %eax
    shrl    $31, %eax
    addl    %edi, %eax
    sarl    %eax
    ret

교대와 유사하게

int shr2signed(int a) {
  return a >> 1;
}

출력 :

    sarl    %edi
    movl    %edi, %eax
    ret

수행중인 작업에 따라 개별 오류를 수정하거나 실제 오류와 비교하여 개별 오류를 유발 하여 추가 코드를 사용하여 수정해야 할 수 있습니다. 원하는 것이 층간 결과라면, 오른쪽 교대는 내가 아는 대안보다 빠르고 쉽습니다.
supercat

바닥이 필요한 경우 "2로 나누기"로 원하는 것을 설명 할 가능성이 낮습니다
Michael Donohue

자연수와 실수의 나눗셈은 (n + d) / d = (n / d) +1이라는 공리를지지합니다. 실수의 나눗셈은 (-n) / d =-(n / d), 자연수로는 무의미한 공리를지지합니다. 정수로 닫히고 두 공리를 유지하는 나누기 연산자를 사용할 수 없습니다. 내 생각에, 첫 번째 공리가 모든 숫자에 대해 유지되어야하고 두 번째 공리가 실수에 대해서만 유지되어야한다는 것은 첫 번째 공리는 정수 또는 실수에 대해서는 유지해야하지만 정수에 대해서는 유지되어야한다는 것보다 더 자연스러운 것 같습니다. 또한 두 번째 공리가 실제로 유용한 경우가 궁금 합니다 .
supercat

1
첫 번째 공리를 만족시키는 정수 나누기 방법은 숫자 라인을 크기의 영역으로 분할합니다 d. 이러한 파티셔닝은 많은 목적에 유용합니다. 0과 -1이 아닌 다른 곳에 중단 점이 있으면 오프셋을 추가하면 이동 점이 이동합니다. 두 번째 공리를 만족시키는 정수 나누기는 숫자 선을 대부분 크기 d는 있지만 하나는 크기 인 영역으로 분할합니다 2*d-1. 정확히 "동일한"부서가 아닙니다. 홀수 볼 파티션이 실제로 유용한 시점에 대한 제안을 제공 할 수 있습니까?
supercat

shr2signed에 대한 컴파일러 출력이 잘못되었습니다. x86의 gcc는 산술 시프트 ( sar)를 사용 하여 >>의 부호있는 정수를 구현하도록 선택합니다 . goo.gl/KRgIkb . 이 메일 링리스트 포스트 ( gcc.gnu.org/ml/gcc/2000-04/msg00152.html )는 gcc가 역사적으로 부호있는 int에 산술 시프트를 사용한다는 것을 확인하므로 FreeBSD gcc 4.2.1이 부호없는 시프트를 사용했을 가능성은 거의 없습니다. 나는 그것을 수정하기 위해 귀하의 게시물을 업데이트했으며 실제로는 둘 다 사용하는 SAR 일 때 두 shr을 사용한다고 말하고 있습니다. SHR은 /사례 의 부호 비트를 추출하는 방법 입니다. 갓 볼트 링크도 포함되어 있습니다.
Peter Cordes

15

추가 된 메모-

x * = 0.5는 일부 VM 기반 언어에서 종종 빠릅니다. 특히 변수를 0으로 나눌 때 검사 할 필요가 없기 때문에 특히 액션 스크립트입니다.


2
@ minitech : 그것은 나쁜 테스트입니다. 테스트의 모든 코드는 일정합니다. 코드가 JIT되기 전에 모든 상수를 제거합니다.

@ M28 : jsPerf의 내부 (즉, eval)가 매번 새롭게 일어나도록 확신했습니다. 그럼에도 불구하고, 그것은 매우 어리석은 최적화이기 때문에 꽤 나쁜 테스트입니다.
Ry-

13

x = x / 2; OR 사용 x /= 2;새로운 프로그래머가 나중에 작업 할 수 있기 때문입니다. 따라서 코드 라인에서 무슨 일이 일어나고 있는지 쉽게 알 수 있습니다. 모든 사람이 그러한 최적화를 인식하지 못할 수 있습니다.


12

나는 프로그램 경쟁의 목적을 말하고있다. 일반적으로 2로 나누기가 여러 번 발생하는 입력이 매우 크며 입력이 양수 또는 음수 인 것으로 알려져 있습니다.

x >> 1은 x / 2보다 낫습니다. 나는 2 개 작업으로 10 ^ 10 이상의 부서가 발생한 프로그램을 실행하여 ideone.com을 확인했습니다. x / 2는 거의 5.5 초, x >> 1은 거의 2.6 초가 소요되었습니다.


1
부호없는 값의 경우 컴파일러는로 최적화해야 x/2합니다 x>>1. 부호있는 값의 경우 거의 모든 구현 은 양수와 같지만 양수일 때 더 빨리 계산 될 수 있고 음수 일 때 와는 다른 x>>1의미를 갖도록 정의 합니다 . x/2xx/2x
supercat

12

몇 가지 고려해야 할 사항이 있습니다.

  1. 비트 시프트를 위해 특별한 계산이 실제로 필요하지 않기 때문에 비트 시프트는 더 빨라야하지만, 지적한 바와 같이 음수에 잠재적 인 문제가 있습니다. 양수를 가지고 있고 속도를 찾고 있다면 비트 시프트를 권장합니다.

  2. 분열 연산자는 사람이 쉽게 읽을 수 있습니다. 따라서 코드 가독성을 찾고 있다면 이것을 사용할 수 있습니다. 컴파일러 최적화 분야는 먼 길을 가졌으므로 코드를 읽고 이해하기 쉽게 만드는 것이 좋습니다.

  3. 기본 하드웨어에 따라 작업 속도가 다를 수 있습니다. Amdal의 법칙은 일반적인 경우를 빠르게하는 것입니다. 따라서 다른 것보다 빠르게 다른 작업을 수행 할 수있는 하드웨어가있을 수 있습니다. 예를 들어 0.5를 곱하는 것이 2를 나누는 것보다 빠를 수 있습니다 (정수 나누기를 강제하려면 곱셈의 바닥을 차지해야 함).

순수한 성능을 유지하고 있다면 수백만 번 작업을 수행 할 수있는 테스트를 만드는 것이 좋습니다. 실행을 여러 번 (샘플 크기) 샘플링하여 OS / 하드웨어 / 컴파일러 / 코드에서 통계적으로 가장 적합한 것을 결정하십시오.


2
"비트 시프트가 더 빨라야합니다." 컴파일러는 비트 시프트로 분할을 최적화합니다
Trevor Hickey

나는 그들이 그렇게되기를 희망하지만, 당신이 컴파일러의 소스에 접근 할 수 없다면, 당신은 확신 할 수 없다 :)
James Oravec

1
또한 구현이 가장 일반적인 방식으로 비트 시프트를 처리하고 음수를 처리하려는 방식과 일치하는 것과 일치 >>하지 않는 것과 일치하는 경우 비트 시프트를 권장합니다 /.
supercat

12

CPU에 관한 한, 비트 시프트 연산은 분할 연산보다 빠릅니다. 그러나 컴파일러는이를 알고이를 가능한 한 적절하게 최적화하므로 코드가 효율적으로 실행되고 있음을 알면 가장 이해하기 쉬운 방식으로 코딩 할 수 있습니다. 그러나 이전에 지적한 이유 unsigned int보다 캔 (일부 경우)을 최적화하는 것이 좋습니다 int. 부호있는 산술이 필요하지 않은 경우 부호 비트를 포함하지 마십시오.


11

x = x / 2; 사용하기에 적합한 코드입니다. 그러나 작업은 출력을 생성하려는 방식에 대한 프로그램에 따라 다릅니다.


11

예를 들어 나누기를 원한다면 x / 2를 사용하고 컴파일러가 연산자 (또는 다른 것)를 이동하도록 최적화하십시오.

오늘날의 프로세서는 이러한 최적화로 인해 프로그램 성능에 영향을 미치지 않습니다.


10

이에 대한 대답은 작업중인 환경에 따라 다릅니다.

  • 당신이 곱셈에 대한 하드웨어 지원없이 8 비트 마이크로 컨트롤러 또는 아무것도에서 작업하는 경우, 비트 이동이 예상과 일상, 그리고 컴파일러는 거의 확실집니다 반면되어 x /= 2x >>= 1, 나눗셈 기호의 존재는 그 환경에 비해 더 많은 눈썹을 올릴 것이다 교대를 사용하여 부서에 영향을 미칩니다.
  • 성능이 중요한 환경이나 코드 섹션에서 작업 중이거나 컴파일러 최적화를 해제 한 상태에서 코드를 컴파일 할 수 있다면 x >>= 1그 이유를 설명하는 주석이 목적을 명확하게하기 위해 최선일 것입니다.
  • 위의 조건 중 하나에 해당되지 않는 경우을 사용하여 코드를 더 읽기 쉽게 만드십시오 x /= 2. 시프트가 더 효율적인 sans 컴파일러 최적화임을 알았다는 것을 불필요하게 증명하는 것보다 코드를 살펴 보는 다음 프로그래머가 시프트 작업에서 10 초의 이중 테이크를 두는 것이 좋습니다.

이들은 모두 부호없는 정수를 가정합니다. 단순한 변화는 아마도 당신이 원하는 것이 아닐 것입니다. 또한 DanielH는 x *= 0.5ActionScript와 같은 특정 언어 에 사용 하는 것에 대한 좋은 지적을 합니다.


8

모드 2, = 1을 테스트합니다. c. 그러나 이것은 가장 빠를 수 있습니다.


7

일반적으로 올바른 변화는 다음과 같습니다.

q = i >> n; is the same as: q = i / 2**n;

이것은 때때로 명료 한 비용으로 프로그램 속도를 높이는 데 사용됩니다. 나는 당신이 그것을해야한다고 생각하지 않습니다. 컴파일러는 속도 향상을 자동으로 수행 할 수있을 정도로 똑똑합니다. 이것은 쉬프트를하는 것이 명확성을 희생시키면서 아무것도 얻지 못한다는 것을 의미합니다 .

Practical C ++ Programming 에서이 페이지를 살펴보십시오 .


예를 들어 최대 (x+128)>>8값에 근접 x하지 않은 값을 계산할 값을 계산하려면 어떻게 이동하지 않고 간단하게 계산할 수 있습니까? 참고 (x+128)/256작동하지 않습니다. 멋진 표현을 알고 있습니까?
supercat

7

분명히 다음 코드를 읽는 사람을 위해 코드를 작성하는 경우 "x / 2"의 명확성을 찾으십시오.

그러나 속도가 목표라면 결과를 시간과 방법에 따라 시도하십시오. 몇 달 전에 저는 정수 배열을 단계별로 실행하고 각 요소를 2로 나누는 비트 맵 컨볼 루션 루틴을 작업했습니다. "x >> 1"을 "x로 대체하는 오래된 트릭을 포함하여 모든 종류의 작업을 수행하여 최적화했습니다. / 2 ".

실제로 두 가지 방법을 모두 사용했을 때 x / 2가 x >> 1보다 빠르다는 사실을 알게되었습니다.

기본 최적화가 설정된 Microsoft VS2008 C ++를 사용하고있었습니다.


4

성능면에서. CPU의 시프트 연산은 분할 연산 코드보다 훨씬 빠릅니다. 따라서 2로 나누거나 2로 곱하면 모두 교대 작업의 이점이 있습니다.

모양과 느낌에 관해서. 엔지니어로서 우리는 아름다운 숙녀조차도 사용하지 않는 화장품에 붙어 있습니다! :)


3

X / Y는 올바른 것입니다. 그리고 ">>"시프 팅 연산자입니다. 두 개의 정수를 나누려면 (/) 피제수 연산자를 사용할 수 있습니다. 시프트 연산자는 비트를 시프트하는 데 사용됩니다.

x = x / 2; x / = 2; 우리는 이렇게 사용할 수 있습니다 ..


0

x >> 1이 x / 2보다 빠르지 만, 음수 값을 처리 할 때 >>를 올바르게 사용하는 것은 조금 더 복잡합니다. 다음과 비슷한 것이 필요합니다.

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.