5

次のように書くことができます。

int i = 3;
int k = 2;
int division = i / k;
int remainder = i % k;

低レベルでは、これは ALU に 2 つのビジョン操作を実行するように要求するように思われます。1 つは商を返し、もう 1 つは剰余を返します。ただし、ALU は 1 回の操作で両方を計算する可能性が高いと思います。その場合、これは最適な効率ではありません。

CPUに2回計算させることなく、より効率的な方法はありますか? つまり、C++ から 1 回の操作で実行できるのでしょうか。

4

5 に答える 5

9

実際、コンパイラはコンパイル時に結果を把握できるため、記述したコードは除算命令を生成しません。小さなテスト プログラムを作成し、コンパイラ (VC++ 10SP1) を設定して、アセンブリ コード リストを生成しました。

#include <iostream>

using namespace std;

struct result {
    long quotient, remainder;
};

result divide(long num, long den) {
    result d = { num / den, num % den };
    return d;
}

int main() {
    result d = divide(3, 2);
    d = divide(10, 3);
    cout << d.quotient << " : " << d.remainder << endl;
    return 0;
}

このように記述し、関数をインライン化しないようにコンパイラーに明示的に指示する必要がありました。そうでなければ、コンパイラーはほとんどのコードを喜んで最適化してしまうでしょう。除算関数の結果のアセンブリ コードを次に示します。

; 8    : result divide(long num, long den) {

  00000 55       push    ebp
  00001 8b ec        mov     ebp, esp

; 9    :     result d = { num / den, num % den };

  00003 99       cdq
  00004 f7 7d 08     idiv    DWORD PTR _den$[ebp]

; 10   :     return d;
; 11   : }

  00007 5d       pop     ebp
  00008 c3       ret     0

単一の IDIV 命令を生成し、それによって生成された商と剰余を使用するのに十分スマートです。最新の C および C++ コンパイラは、この種の最適化が非常に得意です。パフォーマンス上の問題があり、ボトルネックがどこにあるかを判断するためにコードをプロファイリングした場合を除き、コンパイラを推測しようとしないでください。

于 2011-09-14T23:41:43.850 に答える
5

ISO C99 には次のldiv機能があります。

#include <stdlib.h>

ldiv_t ldiv(long numer, long denom);

ldiv() 関数は、値 numer/denom (分子/分母) を計算します。
を含む ldiv_t という名前の構造体で商と剰余を返します。
quot と rem という名前の 2 つの長いメンバー。

単一の操作に還元される FPU レベルかどうかはわかりません。

于 2011-09-14T22:50:46.470 に答える
5

もちろん:

int i = 3;
int k = 2;
int division = i / k;
int remainder = i - division * k;

また、本当にこれをやりたい場合は、を見てくださいdiv。上記のソリューションと同じように、より高速であるとは思えません。

于 2011-09-14T22:48:32.697 に答える
1

組み込みのものは何も知りませんが、除算の代わりに乗算でシミュレートできます。

int division = i / k;
int remainder = i - (division * k);
于 2011-09-14T22:50:23.070 に答える
0

何が最速かを自問するときは、通常、それをベンチマークすることをお勧めします (私はそうするのが好きです)。だから私はここから答えを得て、小さなベンチマークプログラムを書き、それを投げましたgcc(同様の結果が予想されるg++と思います) at -O0(at-O1彼はすべてを最適化し、私のベンチマークは壊れています)。

ラップトップで実行2^28(iおよびkから1まで実行2^14) を実行し、次のランタイムを取得しました。

division = 0;
remainder = 0;
// this test is only there to measure the constant overhead!

1.676秒

division = i/k;
remainder = i%k;

24.614秒

division = i/k;
remainder = i - division*k;

15.009秒

ldiv_t d = ldiv(i,k);
division = d.quot;
remainder = d.rem;

18.845秒

ご覧のとおり、違いあり、最善の方法は乗算アプローチです。アプローチもいいのldivですが、他に比べると少し面倒です。

于 2011-09-15T00:03:43.937 に答える