source

C/C++의 정수 분할의 빠른 상한

goodcode 2022. 8. 30. 22:12
반응형

C/C++의 정수 분할의 빠른 상한

된 " " "x ★★★★★★★★★★★★★★★★★」y, 와 C는 모두 C++의q = x/y★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★를 들어, 「」라고 하는 것은,ceil(10/5)=2 ★★★★★★★★★★★★★★★★★」ceil(11/5)=3.

분명한 접근법에는 다음과 같은 것이 포함됩니다.

q = x / y;
if (q * y < x) ++q;

이 더 제가 본 . 그리고 제가 본 (실제로 사용된) 다른 방법에는 캐스팅이 포함됩니다.float ★★★★★★★★★★★★★★★★★」double추가 곱셈(또는 두 번째 나눗셈)과 분기를 피하고 부동소수점수로 주조하는 것을 피하는 보다 직접적인 방법이 있습니까?

양수일 경우

unsigned int x, y, q;

반올림하려면...

q = (x + y - 1) / y;

또는 (x+y 단위의 오버플로우)

q = 1 + ((x - 1) / y); // if x != 0

양수의 경우:

    q = x/y + (x % y != 0);

부정적이기도 한 x, y가지도 : 1분할만 하다.

int div_ceil(int x, int y) {
    return x / y + (x % y > 0);
}

★★의 경우:x0으로 하다리마인더가 0이 아니면 1을 추가해야 합니다.

ifx나눗셈이이 되고 나눗셈은 0이 되고 나눗셈은 0이 되기 때문에 하지 않겠습니다.그것이 우리가 필요한 것입니다.그리고 우리는 아무것도 추가하지 않을 것입니다.왜냐하면x % y이지 않다

Sparky의 답변은 이 문제를 해결하는 표준적인 방법 중 하나이지만, 제 코멘트에서도 언급했듯이, 당신은 오버플로우의 위험을 안고 있습니다.는 좀 더 풀 수 , 할까요?long longs?s?

Nathan Ernst의 답변은 하나의 솔루션을 제공하지만 함수 호출, 변수 선언 및 조건부로 구성되어 있기 때문에 최적화하기가 어렵기 때문에 OPS 코드보다 짧지 않고 더 느릴 수 있습니다.

저의 솔루션은 다음과 같습니다.

q = (x % y) ? x / y + 1 : x / y;

모듈로와 나눗셈은 프로세서에서 동일한 명령을 사용하여 수행되기 때문에 OPS 코드보다 약간 빠릅니다. 컴파일러는 그것들이 동등하다는 것을 알 수 있기 때문입니다.적어도 gcc 4.4.1은 x86에서 -O2 플래그를 사용하여 이 최적화를 수행합니다.

이론적으로 컴파일러는 Nathan Ernst의 코드로 함수 호출을 인라인화하여 같은 것을 출력할 수 있지만, 제가 테스트했을 때 gcc는 그렇게 하지 않았습니다.컴파일된 코드를 표준 라이브러리의 단일 버전에 연결하기 때문일 수 있습니다.

마지막으로, 최신 머신에서는 이 모든 것이 매우 엄격한 루프 상태에 있고 모든 데이터가 레지스터 또는 L1-캐시에 저장되어 있는 경우를 제외하고 문제가 되지 않습니다.그렇지 않으면 Nathan Ernst를 제외하고 모든 솔루션이 동일하게 빠릅니다. Nathan Ernst는 함수를 메인 메모리에서 가져와야 할 경우 상당히 느려질 수 있습니다.

를 사용할 수 있습니다.divcstdlib로 기능하여 한 번의 호출로 몫과 나머지를 얻은 후 다음과 같이 상한을 개별적으로 처리합니다.

#include <cstdlib>
#include <iostream>

int div_ceil(int numerator, int denominator)
{
        std::div_t res = std::div(numerator, denominator);
        return res.rem ? (res.quot + 1) : res.quot;
}

int main(int, const char**)
{
        std::cout << "10 / 5 = " << div_ceil(10, 5) << std::endl;
        std::cout << "11 / 5 = " << div_ceil(11, 5) << std::endl;

        return 0;
}

나는 차라리 코멘트를 하고 싶었지만 나는 충분히 높은 평판을 가지고 있지 않다.

내가 아는 한, 긍정적인 주장과 2의 거듭제곱인 제수의 경우, 이것이 가장 빠른 방법입니다(CUDA에서 테스트됨).

//example y=8
q = (x >> 3) + !!(x & 7);

일반적인 긍정적인 주장의 경우에만 다음과 같이 하는 경향이 있습니다.

q = x/y + !!(x % y);

부호 있는 정수 또는 부호 없는 정수.

q = x / y + !(((x < 0) != (y < 0)) || !(x % y));

서명된 배당금 및 서명되지 않은 제수.

q = x / y + !((x < 0) || !(x % y));

서명되지 않은 배당금과 서명된 제수를 위해.

q = x / y + !((y < 0) || !(x % y));

부호 없는 정수의 경우.

q = x / y + !!(x % y);

제로 제수가 실패한다(네이티브 조작과 같다).오버플로를 일으킬 수 없습니다.

대응하는 플로어 및 모듈로constexpr필요한 오버로드를 선택하기 위한 템플릿과 함께 구현(완전 최적화 및 부호 비교 경고 불일치 방지):

https://github.com/libbitcoin/libbitcoin-system/wiki/Integer-Division-Unraveled

단순화된 범용 형식,

int div_up(int n, int d) {
    return n / d + (((n < 0) ^ (d > 0)) && (n % d));
} //i.e. +1 iff (not exact int && positive result)

보다 일반적인 답변을 위해 C++는 잘 정의된 반올림 전략으로 정수 나눗셈에 대해 기능합니다.

이는 양수 또는 음수에 적용됩니다.

q = x / y + ((x % y != 0) ? !((x > 0) ^ (y > 0)) : 0);

나머지가 있는 경우는, 가 체크합니다.x그리고.y같은 부호를 가지며 덧붙인다.1따라서.

이건 어때?(y가 음이 아닌 경우는 음이 아니므로 y가 음이 아닌 보증이 없는 변수인 경우는 이 값을 사용하지 마십시오.

q = (x > 0)? 1 + (x - 1)/y: (x / y);

줄였다y/y1대 1로, 그 용어를 없애다x + y - 1넘칠 가능성이 있어요.

나는 피한다x - 1감쌀 때x는 부호 없는 유형으로 0을 포함합니다.

서명필x, 음수와 0은 여전히 하나의 케이스로 결합됩니다.

현대의 범용 CPU에서는 그다지 큰 메리트는 없지만 임베디드 시스템에서는 다른 어떤 정답보다 훨씬 빠릅니다.

컴파일러는 O3로 컴파일하여 최적화가 잘 됩니다.

q = x / y;
if (x % y)  ++q;

언급URL : https://stackoverflow.com/questions/2745074/fast-ceiling-of-an-integer-division-in-c-c

반응형