web-dev-qa-db-ja.com

整数の除算と剰余を取得するC ++の最適な方法

私はちょうど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);

または

多分一度に両方を与える魔法の機能がありますか?

89
Cookie

X86では、残りは除算自体の副産物であるため、半分まともなコンパイラはそれを使用するだけで済みます(また、divを再度実行しないでください)。これはおそらく他のアーキテクチャでも行われます。

命令:DIV src

注:符号なし除算。アキュムレータ(AX)を「src」で除算します。除数がバイト値の場合、結果はAL およびAHに剰余に格納されます。除数がWord値の場合、DX:AXは「src」で除算され、結果はAXに格納されますおよび剰余はDXに格納されます

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

std::div は、結果と剰余の両方を含む構造体を返します。

69
pezcode

少なくとも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
24
Peter Alexander

Div()および除算とmodの組み合わせをテストするサンプルコード。これらをgcc -O3でコンパイルしました。doNothingの呼び出しを追加して、コンパイラーがすべてを最適化するのを停止する必要がありました(部門+ modソリューションの出力は0になります)。

塩の粒でそれを取る:

#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

9
Greg Howell

前述のstd :: div関数ファミリーに加えて、std :: remquo関数ファミリ、rem-ainderを取得して取得渡されたポインターを介したquo-tient。

[編集:]結局、std :: remquo 実際には商を返さない のように見える。

5
Jamin Grey

ここでは、32ビットのインテルプラットフォームで64ビット整数を使用してg ++ 4.6.3を信頼することはできません。 a/bはdivdi3の呼び出しによって計算され、a%bはmoddi3の呼び出しによって計算されます。これらの呼び出しでa/bおよびa-b *(a/b)を計算する例さえ考え出すことができます。そこで、c = a/bとa-b * cを使用します。

Divメソッドはdiv構造を計算する関数の呼び出しを行いますが、関数呼び出しは、整数型のハードウェアサポートがあるプラットフォーム(つまり、64ビットIntel/AMDプラットフォームの64ビット整数)では効率が悪いようです。

3
brac37

他のすべてが等しい場合、最良の解決策は、あなたの意図を明確に表現するものです。そう:

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

おそらく、あなたが提示した3つのオプションの中で最高です。ただし、他の回答で述べたように、divメソッドは両方の値を一度に計算します。

3
Jon