source

정수를 2로 나눌 때 어떤 옵션을 사용하는 것이 더 좋을까요?

factcode 2022. 9. 16. 22:59
반응형

정수를 2로 나눌 때 어떤 옵션을 사용하는 것이 더 좋을까요?

다음 중 정수를 2로 나누는 가장 좋은 방법은 무엇이고 그 이유는 무엇입니까?

기술 1:

x = x >> 1;

기술 2:

x = x / 2;

여기서x는 정수입니다.

실행하려는 작업에 가장 적합한 작업을 사용하십시오.

  • 숫자를 일련의 비트로 취급하는 경우는, 비트 시프트를 사용합니다.
  • 수치로 취급하는 경우는, 나눗셈을 사용합니다.

이 두 가지가 완전히 동일하지는 않다는 점에 유의하십시오.음의 정수에 대해 서로 다른 결과를 얻을 수 있습니다.예를 들어 다음과 같습니다.

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

(이상)

첫 번째는 나누는 것처럼 보이나요?아니요. 분할하려면x / 2컴파일러는 가능하면 비트 시프트를 사용하도록 최적화할 수 있습니다(강도의 저감이라고 합니다).이것에 의해, 독자적으로 실시하면, 마이크로 최적화는 불필요하게 됩니다.

덧붙이자면, 사용하는 것을 선호하는 이유는 매우 많습니다.x = x / 2;다음은 몇 가지 예입니다.

  • 그것은 당신의 의도를 더 명확하게 표현한다(레지스터 비트 같은 것을 약간 만지작거리고 있지 않은 경우).

  • 컴파일러는 어쨌든 이것을 시프트 조작으로 줄일 것이다.

  • 컴파일러가 그것을 줄이지 않고 시프트보다 느린 동작을 선택했다고 해도, 이것이 프로그램의 퍼포먼스에 측정 가능한 방법으로 영향을 줄 가능성은 매우 작다(그리고 그것이 측정 가능한 영향을 준다면, 시프트를 사용할 실제 이유가 있다).

  • 나눗셈이 더 큰 식에 포함될 경우 나눗셈 연산자를 사용하면 우선 순위를 얻을 수 있습니다.

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
    
  • 서명된 산술은 위에서 언급한 우선순위 문제보다 더 복잡한 문제를 일으킬 수 있다

  • 다시 말씀드리지만 컴파일러는 이미 이 작업을 수행합니다.실제로 2의 거듭제곱뿐만 아니라 모든 종류의 숫자에 대해 정수 나눗셈을 일련의 이동, 덧셈 및 곱셈으로 변환합니다.이에 대한 자세한 내용은 이 질문을 참조하십시오.

간단히 말해, 실제로 곱셈이나 나누기를 의도할 때 교대조를 코딩하여 아무것도 사지 않는 것입니다. 단, 버그가 발생할 가능성이 높아집니다.컴파일러가 적절할 때 이러한 작업을 최적화할 수 있을 만큼 똑똑하지 못한 것은 평생의 일입니다.

divide만 사용하세요./보다 명확하다고 가정합니다.그에 따라 컴파일러가 최적화됩니다.

정수를 2로 나누는 가장 좋은 선택지와 이유는 무엇입니까?

그게 무슨 뜻이냐에 따라 다르죠

동료들이 당신을 싫어하거나 코드를 읽기 어렵게 만들고 싶다면 첫 번째 옵션을 선택하겠어요.

숫자를 2로 나누려면 두 번째 숫자로 하세요.

이 두 가지는 동일하지 않습니다.숫자가 음수이거나 큰 식 안에 있는 경우 동작은 동일하지 않습니다.비트 시프트는 비트 시프트보다 우선도가 낮습니다.+또는-, division의 우선순위가 높아집니다.

그 의도가 무엇인지 표현하기 위해 코드를 작성해야 합니다.퍼포먼스가 걱정되는 경우라면 걱정하지 마세요.옵티마이저는 이러한 종류의 미세 최적화를 훌륭하게 수행할 수 있습니다.

당신이 찬성해야 할 다른 답변에 동의합니다.x / 2왜냐하면 그 의도가 더 명확하고 컴파일러가 당신을 위해 최적화해야 하기 때문입니다.

단, 다른 이유로는x / 2에 걸쳐서x >> 1의 행동이>>구현에 의존합니다.x서명된int음성이에요.

섹션 6.5.7에서 ISO C99 표준의 항목 5:

의 결과E1 >> E2E1오른쪽 방향의E2비트 위치한다면E1부호 없는 타입을 가지거나E1부호 있는 유형과 음이 아닌 값을 가지며, 결과의 값은 의 몫의 정수 부분입니다.E1/ 2E2. 만약E1는 부호 있는 유형과 음의 값을 가지며, 결과 값은 구현 정의됩니다.

Knuth는 말했다:

섣부른 최적화는 모든 악의 근원이다.

그래서 나는 그것을 사용하는 것을 추천한다.x /= 2;

이렇게 하면 코드를 쉽게 이해할 수 있을 뿐만 아니라 프로세서에 큰 차이는 없다고 생각합니다.

대회를 프로그래밍할 목적으로 말씀드리는 겁니다.일반적으로 2로 나누기가 여러 번 일어나고 입력이 양수 또는 음수인 것으로 알려진 매우 큰 입력이 있습니다.

x>1이 x/2보다 낫다.ideone.com에서 10^10 나눗셈 2개 이상의 연산이 이루어지는 프로그램을 실행하여 확인하였습니다.x/2는 5.5초 가까이 소요된 반면 x>1은 같은 프로그램에서 거의 2.6초 소요되었습니다.

컴파일러의 출력을 참조해 주세요.x86-64에서 이 테스트를 실행했습니다.
gcc (GCC) 4.2.1 20070719 [FreeB]SD]

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

x / 2더 선명하고x >> 1(마이크로 벤치마크에 따르면 Java JVM의 경우 약 30% 더 빠릅니다)다른 사람들이 지적한 바와 같이 음수의 경우 반올림이 약간 다르므로 음수를 처리하려면 이 점을 고려해야 합니다.일부 컴파일러는 자동으로 변환됩니다.x / 2로.x >> 1만약 그들이 그 숫자가 음수가 될 수 없다는 것을 안다면(내가 이것을 확인할 수 없다고 생각해도).

심지어.x / 2(저속) 분할 CPU 명령을 사용할 수 없는 경우가 있습니다.이것은 일부 숏컷이 가능하지만, 아직 CPU 명령보다 느리기 때문입니다.x >> 1.

(이것은 C/C++ 질문입니다.다른 프로그래밍 언어에는 연산자가 더 많습니다.Java의 경우 부호 없는 오른쪽 시프트도 있습니다.x >>> 1이 역시 다릅니다.두 값의 평균(평균) 값을 정확하게 계산할 수 있습니다.(a + b) >>> 1매우 큰 값이라도 평균값을 반환한다.a그리고.b이는 예를 들어 배열 인덱스가 매우 커질 수 있는 바이너리 검색에서 필요합니다.바이너리 검색의 많은 버전에는 버그가 있었습니다.왜냐하면 그들은(a + b) / 2평균을 계산합니다.이건 제대로 작동하지 않아요.올바른 해결방법은(a + b) >>> 1대신).

메모 추가 -

x *= 0.5는 일부 VM 기반 언어(특히 변수가 0으로 나누어져 있는지 확인할 필요가 없으므로 작업 스크립트)에서 더 빠릅니다.

사용하다x = x / 2;또는x /= 2;장래에 새로운 프로그래머가 작업할 가능성이 있기 때문입니다.그래서 그가 코드 라인에서 무슨 일이 일어나고 있는지 알아내는 것이 더 쉬울 것이다.누구나 이러한 최적화를 인식하지 못할 수 있습니다.

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

  1. 비트 시프트는 비트 시프트에 특별한 연산이 필요하지 않기 때문에 더 빨라야 하지만 지적했듯이 음수에 문제가 있을 수 있습니다.만약 당신이 확실히 양수를 가지고 있고 속도를 찾고 있다면, 나는 bitsshift를 추천한다.

  2. 나눗셈 연산자는 사람이 읽기 매우 쉽다.따라서 코드를 읽기 쉽게 하려면 이 방법을 사용할 수 있습니다.컴파일러 최적화 분야는 크게 발전했기 때문에 코드를 쉽게 읽고 이해할 수 있도록 하는 것이 좋습니다.

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

순수 성능을 원하는 경우 수백만 번 연산을 수행할 수 있는 테스트를 만들 것을 권장합니다.OS/하드웨어/컴파일러/코드에 통계적으로 가장 적합한 실행을 여러 번 샘플 크기(샘플 크기)로 확인합니다.

CPU에 관한 한 비트 시프트 동작은 분할 동작보다 빠릅니다.다만, 컴파일러는 이것을 알고 있기 때문에, 가능한 한 적절히 최적화할 수 있기 때문에, 가장 적절한 방법으로 코드를 코드화할 수 있어 코드가 효율적으로 동작하고 있는 것을 알 수 있어 안심할 수 있습니다.하지만 기억해라unsigned int(경우에 따라서는) 보다 최적화할 수 있다int앞서 지적한 이유로서명된 산술이 필요하지 않은 경우 기호 비트를 포함하지 마십시오.

x = x / 2; 이 사용하기에 적합한 코드입니까?다만, 조작은, 출력의 작성 방법에 관한 독자적인 프로그램에 의해서 다릅니다.

당신의 의도를 명확히 하세요...예를 들어, 분할하려면 x / 2를 사용하고, 컴파일러가 연산자(또는 다른 것)를 이동시키도록 최적화합니다.

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

이에 대한 답은 작업 환경에 따라 달라집니다.

  • 8비트 마이크로컨트롤러 또는 멀티플리케이션을 하드웨어로 지원하지 않는 경우 비트 시프트가 예상되며 컴파일러는 거의 확실하게 동작합니다.x /= 2안으로x >>= 1나눗셈 기호의 존재는 나눗셈을 하기 위해 시프트를 사용하는 것보다 그 환경에서 더 많은 눈살을 찌푸리게 할 것이다.
  • 퍼포먼스가 중요한 환경이나 코드의 일부에서 작업하고 있는 경우, 또는 컴파일러 최적화가 꺼진 상태에서 코드를 컴파일 할 수 있는 경우,x >>= 1그 이유를 설명하는 코멘트와 함께 아마도 목적을 명확히 하기 위해서만 최선일 것이다.
  • 위의 조건 중 하나에 해당하지 않는 경우 단순히 다음을 사용하여 코드를 보다 쉽게 읽을 수 있도록 합니다.x /= 2시프트가 더 효율적인 산스 컴파일러 최적화라는 것을 불필요하게 증명하기보다는 당신의 시프트 작업을 10초간 더블테이크 하는 다음 프로그래머를 구하는 것이 좋습니다.

이 모든 것은 부호 없는 정수를 가정합니다.간단한 교대 근무는 서명에 적합하지 않을 수 있습니다.또한, DanielH는 DanielH를 사용하는 것에 대해 좋은 점을 제기합니다.x *= 0.5ActionScript와 같은 특정 언어용입니다.

mod 2, = 1. c.의 구문을 모르지만 이 방법이 가장 빠를 수 있습니다.

일반적으로 오른쪽 시프트 분할:

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

이것은 때때로 명확성을 희생하면서 프로그램의 속도를 높이기 위해 사용됩니다.당신이 그걸 꼭 해야하는건 아니라고 생각해요.컴파일러는 자동적으로 스피드 업을 실행할 수 있을 만큼 스마트합니다.즉, 시프트를 도입하는 것은 명확성을 희생하는 것이 아닙니다.

Practical C++ Programming의 이 페이지를 참조해 주세요.

다음 번 읽는 사람을 위해 코드를 작성하는 경우 "x/2"의 선명도를 선택하십시오.

하지만, 속도가 목표라면, 두 가지 방법으로 시도하고 결과를 측정해 보세요.몇 달 전에 저는 비트맵 컨볼루션 루틴을 작성했습니다.정수의 배열을 밟아 각 요소를 2로 나누는 것입니다.최적화를 위해 x/2를 x>1로 대체하는 구태의연한 수법을 구사했습니다.

실제로 양쪽의 타이밍을 측정했을 때 놀랍게도 x/2가 x>1보다 빨랐습니다.

이것은 Microsoft VS2008 C++ 를 사용해 디폴트의 최적화를 유효하게 하고 있습니다.

퍼포먼스 면에서는요.CPU의 시프트 조작은, 분할 op-code보다 큰폭으로 고속입니다.따라서 2로 나누거나 2로 곱하는 것은 모두 교대조 운영의 이점을 얻을 수 있습니다.

모양과 느낌에 대해서요.엔지니어로서 우리는 언제부터 아름다운 여성도 사용하지 않는 화장품에 애착을 갖게 되었을까! :)

X/Y는 올바른 연산자이고 ">>" 시프트 연산자..2를 정수로 나누려면 (/) 배당 연산자를 사용할 수 있습니다.시프트 연산자는 비트를 이동하는 데 사용됩니다.

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

x>1은 x/2보다 빠르지만 음의 값을 처리할 때 >>를 적절하게 사용하는 것은 조금 복잡합니다.다음과 같은 것이 필요합니다.

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}

언급URL : https://stackoverflow.com/questions/10681375/which-is-better-option-to-use-for-dividing-an-integer-number-by-2

반응형