2010-12-30 10 views

答えて

24

はい。 C99§7.20.6.2/ 2は言う:

divldiv、およびlldiv、機能は、単一の操作でnumer/denomnumer % denomを計算します。 あなたが同時に商と剰余の両方を計算したい場合は/%演算子を使用するよりも速いとをはずの

+0

ありがとうございます、http://www.cppreference.com/wiki/numeric/c/divも私のためにそれをクリアしました。商と余りが足りなかった。 – Sheraz

9

+1

édéric:「*より速くなると思っています」と言ったとき、時にはそうでないことを意味していますか? –

+1

@Stuart、まったく、@ジョナサン・ウッドの答えとその後のコメントを見てください。基本的には、パフォーマンスを向上させるために 'ldiv'ファミリーの関数を使うことを考えているのであれば、コンパイラーは関数呼び出しよりも安価なもので'商+余剰 '計算を最適化するほどスマートではないと思っていますあるいは 'ldiv'とそのilkが実際の関数ではなくコンパイラの組み込み関数であることを期待しています。 –

+6

@Stuart Golodetz:XがYよりも速くなければならないことを強制することは、YがXよりも遅くなければならないことを要求することと同じです。高速化を保証する唯一の方法は、 '/'と '%'の両方を計算するコードを見てオプティマイザがインプリメンテーションで 'ldiv'で使用する最適化を使用することを禁止することです。しかし、なぜコンパイラはそれ自体を制限すべきですか? –

21

考えられるのは、/と%の結果は、プロセッサ上の単一のDIV命令から判断できるということです。したがって、歴史的には、div()は両方を取得するための最適な方法を提供するために使用されます。

しかし、新しいコンパイラが/と%演算を1つの除算に最適化できることがわかりました。たとえば、私はMicrosoft Visual C++でこの最適化を見てきました。このような場合、div()は実際には利点がありませんし、実際には呼び出しがあっても遅くなることさえあります。

+3

+1は明示的に 'それを気にしないでください。あなたのコンパイラはおそらくそれよりも賢いでしょう。今私はちょうど私の答えにイタリックを追加します;) –

+7

私は、1つのプロセッサの命令に連続%と/演算を最適化するために十分スマートな任意のコンパイラは、div() 。 :/ –

+1

一部のコンパイラー・オプションを* not *インライン標準ライブラリー呼び出しに設定していない限り、なぜ私はそのようなオプションが存在すると思いますか?おそらく私はCでプログラミングをしていた日に戻ってきましたか? –

2

短い答え:現代の環境では実際にはありません。

人々はなぜdivの利益が弱いのか、または存在しないのかを説明しました。 実際にはさらに悪いです:divと友人は良い練習を傷つけるタイプカップリングを導入しています(下記の欠点のセクションを参照)。

利点:おそらくなし

としてはdivの代わりに、/%はおそらく操作のみアセンブリレベルで一度行われていることを保証を呼び出し、他の回答で述べています。

しかし、最も近代的な文脈で:

  1. CPUはとてもうまく回の計算で行わ膨大な数の最も内側のループを除いて、操作を繰り返して打つ任意のパフォーマンスは、おそらくより道小さいことを数学的な操作を最適化しました他のパフォーマンスヒット(他のコードのように、キャッシュミスなど) =>divの便益がある場合は、それは通常無視されます。
  2. /%を使用する場合でも、現代のコンパイラは、商とそれ以外の部分を取り出して1つの除算命令を生成するだけで正しいことを行います。 =>divの実際のメリットはありません。

欠点:DIVと友人は数字(例えばintまたはlong)の正確なタイプ、静的に(つまり、知られている場合は、あなたのコードが明示的に長い常にint型を使用したり、特定のタイプ

にあなたのコードを結びつけます)、をintに、ldivを長く使っても問題ありません。

しかし、あなたは小さな部品が不要な仮定を避けるには、プログラミングの良い練習に従うならば、あなたはすぐにdivldivネクタイ種類それぞれintまたはlongのコードを使用していることを実感。反対に、/%は、実際に使用されているタイプに合わせて自動的に調整され、コードクリーナーを維持します。

これは、2つの場合には特に表示されている:あなたは抽象離れて実際の型にtypedefを使用

  • からdivも、Cで不器用です!
  • テンプレートを使用して実際のタイプを抽象化します。divはテンプレートを破ります。

以下のサンプルでは、​​「/」を使用してそのコードを示しており、divを使用してコードや友人になるのに対し、「%」は、クリーンシンプル、かつintに縛られない、長い、長い長いまたは何でもあります不器用です。

Testing with int 
my_math_func_div_WRONG says 6 
my_math_func_OVERKILL says 6 // Works but overkill cast to long 
my_math_func_GOOD says 6  // No div no headache. 

Testing with int in long type 
my_math_func_div_WRONG says 6 
my_math_func_OVERKILL says 6 // Works but overkill cast to long 
my_math_func_GOOD says 6  // No div no headache. 

Testing with actual long 
my_math_func_div_WRONG says 70503280 // FAIL 
my_math_func_OVERKILL says 500000006 
my_math_func_GOOD says 500000006  // No div no headache. 

ソースコード:

#include <iostream> 

// '/' and '%' are smart about type. 
// This code is simple and will work with int, long, longlong, char, whatever. 
template<typename T> 
T my_math_func_GOOD(T number) 
{ 
    T quotient = number/10; 
    T remainder = number % 10; 
    // do something 
    return quotient + remainder; 
} 

// div and friends are not smart about type. 
// How do you write code smart about type with them ? 

// Plus adds dependency on C's stdlib. 
#include <stdlib.h> 
template<typename T> 
T my_math_func_div_WRONG(T number) 
{ 
    // This will always downcast to int. Defeats purpose of template. 
    div_t result = div(number, 10); 
    T quotient = result.quot; 
    T remainder = result.rem; 
    // do something 
    return quotient + remainder; 
} 

template<typename T> 
T my_math_func_OVERKILL(T number) 
{ 
    // This will always cast to long (up from int, OVERKILL, possibly down from long long, FAIL). Defeats purpose of template. 
    ldiv_t result = ldiv(number, 10); 
    T quotient = result.quot; 
    T remainder = result.rem; 
    // do something 
    return quotient + remainder; 
} 

template<typename T> 
void my_math_func_test(T number) 
{ 
    T n; 
    n = my_math_func_div_WRONG(number); 
    std::cout << "my_math_func_div_WRONG\tsays " << n << std::endl; // writes 6 
    n = my_math_func_OVERKILL(number); 
    std::cout << "my_math_func_OVERKILL\tsays " << n << std::endl; // writes 6 
    n = my_math_func_GOOD(number); 
    std::cout << "my_math_func_GOOD\tsays " << n << std::endl; // writes 6 
} 

// C99 allows absence of int argc, char **argv 
int main() 
{ 
    std::cout << std::endl << "Testing with int" << std::endl; 
    my_math_func_test<int>(42); 
    std::cout << std::endl << "Testing with int in long type" << std::endl; 
    my_math_func_test<long>(42); 
    std::cout << std::endl << "Testing with actual long" << std::endl; 
    my_math_func_test<long>(5000000042); 
    // std::cout << std::endl << "Testing with long long" << std::endl; 
    // my_math_func_test<long long>(50000000000000000042); 
} 
1

さて、これは古いですが、私はちょうどここでつまずきました。ここで最も重要な違いは、div()の結果が定義されていることです。 C標準は商を丸める方法を述べていません。これは、コンパイラがCPUに依存するマシンの実装を使用できる必要があるためです。 2つの異なる実装が存在する: - 無限に向けて丸める - 0に向かって丸める 。

div()ですが、後者を行うために指定されているため、移植性があります。

関連する問題