C ++ 정수 나누기와 나머지를 얻는 가장 좋은 방법


103

a를 b로 나누고 결과 c와 나머지 모두에 관심이 있는지 궁금합니다 (예 : 초 수를 가지고 있고이를 분과 초로 나누고 싶다고 가정 해 보겠습니다). 가장 좋은 방법은 무엇입니까? 그것에 대해?

일 것이다

int c = (int)a / b;
int d = a % b;

또는

int c = (int)a / b;
int d = a - b * c;

또는

double tmp = a / b;
int c = (int)tmp;
int d = (int)(0.5+(tmp-c)*b);

또는

한 번에 둘 다주는 마법의 기능이 있을까요?


5
아래의 모든 답변은 합리적으로 보입니다. double(마지막 항목)을 사용하는 것은 나에게 나쁜 생각처럼 보인다는 점을 추가하고 싶습니다. 정렬되지 않은 숫자로 끝날 것이고 성능에 비용이들 수 있습니다. 실행 가능한 크기 (특정 임베디드 시스템에서 항상 문제였습니다).
nhed aug.

2
세 번째는 BAD 옵션입니다. tmp = 54.999999999999943157이면 어떨까요? 즉, 구식 캐스팅은 결코 영리한 일이 아닙니다.
jimifiki

답변:


98

x86에서 나머지는 부서 자체의 부산물이므로 반 정도의 컴파일러는 그것을 사용할 수 있어야합니다 ( div다시 수행하지 않음 ). 이것은 아마도 다른 아키텍처에서도 수행 될 것입니다.

지시 : DIVsrc

참고 : 부호없는 나누기. 누산기 (AX)를 "src"로 나눕니다. 제수가 바이트 값이면 결과는 AL에 배치 되고 나머지는 AH에 배치 됩니다. 제수가 워드 값이면 DX : AX를 "src"로 나누고 결과는 AX에 저장 되고 나머지는 DX에 저장됩니다 .

int c = (int)a / b;
int d = a % b; /* Likely uses the result of the division. */

9
많은 분들이 초등학교 때부터 디비전을하면 나머지는 공짜로 받는다는 걸 아시는 분들이 많으 실 것 같아요. 진짜 질문은 우리의 컴파일러가 이것을 활용하기에 충분히 똑똑합니까?

1
@jdv : 놀라지 않을 것입니다. 매우 간단한 최적화입니다.
Jon Purdy

68
빠른 테스트를 시도했습니다. -O2를 사용하는 g ++ 4.3.2에서 어셈블러 출력은 하나의 idivl명령어를 사용하고 결과를 eax 및 edx로 사용 하여 명확하게 표시합니다 . 그렇지 않으면 충격을 받았을 것입니다.
Fred Larson

2
@EuriPinhollow x86에 대한 질문이 아니라는 데 동의합니다. 나는 다른 아키텍처가 아마도 비슷한 일을 할 것이라는 매우 모호한 가정과 함께 그것을 예로 들었습니다.
cnicutar

3
그래도 컴파일러에게 최소한 g ++에 대해 최적화하도록 지시해야합니다. x86_64에서 g ++ 6.3.0을 사용한 간단한 실험에서 최적화가 전혀 없으면 두 개의 idivl명령이 표시되지만 -O1그 이상을 사용하면 하나가 표시됩니다. 매뉴얼에서 "최적화 옵션없이… 문은 독립적입니다"라고 말합니다 .
Tom Zych

80

std::div 결과와 나머지가 모두있는 구조를 반환합니다.


5
이것이 현대 컴파일러의 옵션 1보다 실제로 더 효율적인지 알고 싶습니다.
Greg Howell

2
좋아, 몰랐어. 더 빠릅니까?

좋은. 어딘가에서 오랫동안 구현되었는지 알고 있습니까?
Cookie

@Cookie : C ++ 03에는 개념이 long long없지만 컴파일러가 확장으로 long long오버로드를 가질 가능성이 큽니다 std::div.
ildjarn

@Greg : 메모리의 구조에 결과를 쓰고 반환해야 할 가능성이 가장 높기 때문에 (인라인으로 컴파일하고 구조체 액세스가 최적화되지 않는 한) 수행하는 것보다 더 큰 패널티라고 생각하지 않습니다. 추가 분할 지시.
pezcode

28

적어도 x86에서 g ++ 4.6.1은 IDIVL을 사용하고 단일 명령어에서 둘 다 가져옵니다.

C ++ 코드 :

void foo(int a, int b, int* c, int* d)
{
  *c = a / b;
  *d = a % b;
}

x86 코드 :

__Z3fooiiPiS_:
LFB4:
    movq    %rdx, %r8
    movl    %edi, %edx
    movl    %edi, %eax
    sarl    $31, %edx
    idivl   %esi
    movl    %eax, (%r8)
    movl    %edx, (%rcx)
    ret

순서가 중요합니까? 예를 들어 /=-를 반복하는 경우 분할을 먼저 유지하려면 임시 변수를 사용해야 할 수 있습니다.
AnnanFay

@Annan 그때는 중요하지 않았고 요즘에는 그렇지 않습니다. 컴파일러는 재정렬 할만큼 똑똑합니다.
사사 해

10

div () 및 결합 된 division 및 mod를 테스트하는 샘플 코드. 나는 이것을 gcc -O3로 컴파일했고, 컴파일러가 모든 것을 최적화하는 것을 막기 위해 doNothing에 대한 호출을 추가해야했다.

소금 한 알과 함께 가져 가십시오.

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>

extern doNothing(int,int); // Empty function in another compilation unit

int main() {
    int i;
    struct timeval timeval;
    struct timeval timeval2;
    div_t result;
    gettimeofday(&timeval,NULL);
    for (i = 0; i < 1000; ++i) {
        result = div(i,3);
        doNothing(result.quot,result.rem);
    }
    gettimeofday(&timeval2,NULL);
    printf("%d",timeval2.tv_usec - timeval.tv_usec);
}

출력 : 150

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>

extern doNothing(int,int); // Empty function in another compilation unit

int main() {
    int i;
    struct timeval timeval;
    struct timeval timeval2;
    int dividend;
    int rem;
    gettimeofday(&timeval,NULL);
    for (i = 0; i < 1000; ++i) {
        dividend = i / 3;
        rem = i % 3;
        doNothing(dividend,rem);
    }
    gettimeofday(&timeval2,NULL);
    printf("%d",timeval2.tv_usec - timeval.tv_usec);
}

출력 : 25



3

다른 모든 것이 동일 할 때 최상의 솔루션은 의도를 명확하게 표현하는 것입니다. 그래서:

int totalSeconds = 453;
int minutes = totalSeconds / 60;
int remainingSeconds = totalSeconds % 60;

당신이 제시 한 세 가지 옵션 중 최고 일 것입니다. 그러나 다른 답변에서 언급 했듯이이 div방법은 한 번에 두 값을 모두 계산합니다.


3

32 비트 인텔 플랫폼에서 64 비트 정수로 g ++ 4.6.3을 신뢰할 수 없습니다. a / b는 divdi3에 대한 호출로 계산되고 a % b는 moddi3에 대한 호출로 계산됩니다. 이러한 호출로 a / b 및 ab * (a / b)를 계산하는 예제를 생각해 볼 수도 있습니다. 그래서 저는 c = a / b와 ab * c를 사용합니다.

div 메소드는 div 구조를 계산하는 함수에 대한 호출을 제공하지만, 정수 유형에 대한 하드웨어 지원이있는 플랫폼에서는 함수 호출이 비효율적으로 보입니다 (예 : 64 비트 인텔 / AMD 플랫폼에서 64 비트 정수).


-4

나머지를 얻기 위해 계수를 사용할 수 있습니다. @cnicutar의 대답은 더 깨끗하고 직접적으로 보입니다.


2
예, 원래 포스터는 모듈러스 연산자를 사용했습니다. 문제는이를 효율적으로 만드는 방법입니다.
Mark Lakata 2011
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.