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思われます。(最後の項目)をいじくり回すのは悪い考えのように思えます。数字が揃わず、パフォーマンスが低下する可能性があります。実行可能サイズ(特定の組み込みシステムでは常に問題でした)。
2011

2
3番目は悪いオプションです:tmp = 54.999999999999943157157の場合はどうなりますか?つまり、古いスタイルのキャスティングは決して賢いことではありません。
jimifiki 14

回答:


98

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

指示:DIVsrc

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

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

9
小学校から、割り算をすると残りが無料になることを多くの人が知っていると思います。本当の質問は、私たちのコンパイラはこれを利用するのに十分スマートですか?

1
@jdv:私は驚かないでしょう。これは非常に単純な最適化です。
ジョンパーディ2011

68
簡単なテストをしてみました。-O2を使用するg ++ 4.3.2では、アセンブラーの出力は、1つのidivl命令を使用してeaxとedxで結果を使用してそれを明確に示します。そうでなかったら私はショックを受けたでしょう。
Fred Larson

2
@EuriPinhollow同意するこれはx86に関する質問ではありません。他のアーキテクチャがおそらく同じようなことをするだろうという非常に疑わしい仮定の下で、私はそれを一例としてのみ与えました。
cnicutar 2017

3
ただし、少なくともg ++向けに最適化するようコンパイラーに指示する必要があります。x86_64でg ++ 6.3.0を使用した簡単な実験により、最適化をまったく行わないと2つのidivl命令が得られますが、-O1それ以上では1つしか得られないことがわかりました。マニュアルにあるように、「最適化オプションなしで…ステートメントは独立しています」
トムZych

80

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


5
これが、たとえば最近のコンパイラのオプション1よりも実際に効率的であるかどうか知りたいです。
グレッグハウエル

2
いいですね、知りませんでした。速いですか?

いいね。どこかに長い間実装されているかどうかを知っていますか?
Cookie

@Cookie:C ++ 03にはの概念はありませんlong longが、コンパイラに拡張機能としてのlong longオーバーロードがある可能性が高いstd::divです。
ildjarn 2011

@Greg:メモリ内の構造に結果を書き込み、それを返す必要がある可能性が高いことを考えると、そうではないと思います(インラインとしてコンパイルされ、構造体アクセスが最適化されていない限り)実行するよりも大きなペナルティ追加の除算命令。
pezcode 2011

28

少なくともx86では、g ++ 4.6.1はIDIVLを使用し、その1つの命令から両方を取得します。

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

順序は重要ですか?たとえば、aを反復している/=場合、最初に除算を維持するために一時変数を使用する必要がある場合があります。
AnnanFay 2016

@Annan当時は問題ではありませんでしたし、現在は問題ではありません。コンパイラはそれを並べ替えるのに十分スマートです。
Sahsahae、

10

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



3

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

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

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


3

ここでは、32ビットのIntelプラットフォームで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、2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.