2010-12-30 3 views

Antwort

24

Ja. §7.20.6.2 C99/2 sagt:

Die div, ldiv und lldiv, Funktionen berechnen numer/denom und numer % denom in einem einzigen Vorgang.

+0

Danke, http://www.cppreference.com/wiki/numeric/c/div hat es auch für mich geklärt. fehlte der Quotient und der Rest. – Sheraz

9

Die ist angeblich schneller sein als die / und % Operatoren, wenn Sie sowohl die Quotienten und den Rest zur gleichen Zeit berechnen wollen.

+1

édéric: Wenn du sagst "* soll * schneller sein", ist die Implikation, dass es manchmal nicht ist? –

+1

@Stuart, absolut, siehe @ Jonathan Woods Antwort und mein anschließender Kommentar. Wenn Sie die 'ldiv'-Familie von Funktionen verwenden, um die Leistung zu erhöhen, denken Sie, dass Ihr Compiler nicht schlau genug ist, Ihre' Quotienten + Rest'-Berechnung mit etwas * billigerem * als einem Funktionsaufruf zu optimieren , oder Sie erwarten, dass 'ldiv' und seine Art Compiler intrinsics und nicht tatsächliche Funktionen sind. –

+6

@Stuart Golodetz: Die Vorgabe, dass X schneller als Y sein muss, ist gleichbedeutend mit der Forderung, dass Y * langsamer * als X sein muss, was kein gutes Geschäft ist. Der einzige Weg, um sicherzustellen, dass es schneller ist, wäre zu verbieten, dass der Optimierer, wenn er Code sieht, der sowohl '/' als auch '%' berechnet, keine Optimierungen verwenden kann, die die Implementierung in 'ldiv' verwendet. Aber warum sollte sich ein Compiler so einschränken? –

21

Die Idee ist, dass die Ergebnisse von/und% aus einer einzigen DIV-Anweisung auf dem Prozessor ermittelt werden können. Daher wird div() historisch verwendet, um eine optimierte Möglichkeit zu bieten, beides zu erhalten.

Allerdings habe ich festgestellt, dass neuere Compiler in der Lage sind, eine/und% -Operation auf eine einzige Teilung zu optimieren. Zum Beispiel habe ich diese Optimierung für Microsoft Visual C++ gesehen. In diesen Fällen bietet div() wirklich keinen Vorteil und kann sogar langsam sein, wenn ein Anruf involviert ist.

+3

+1 für eine explizite 'nicht einmal damit zu tun, ist Ihr Compiler wahrscheinlich schlauer als das sowieso '. Jetzt werde ich einfach gehen und kursiv zu meiner Antwort hinzufügen;) –

+7

Ich würde denken, dass jeder Compiler, der schlau genug ist, um aufeinanderfolgende% und/Operationen in einem einzigen Prozessor Befehl zu optimieren, auch intelligent genug ist, um einen Aufruf an div() . :/ –

+1

Es sei denn, Sie setzen eine Compileroption auf * nicht * Inline-Standardbibliotheksaufrufe. Warum denke ich, dass eine solche Option existiert ... vielleicht von damals, als ich in C programmiert habe? –

2

Kurze Antwort: nicht wirklich in einer modernen Umgebung.

Die Leute haben erklärt, warum der Nutzen von div schwach oder gar nicht existent ist. Es ist eigentlich noch schlimmer: div und Freunde führen Typ Kupplung, die gute Praxis verletzt (siehe Abschnitt unten).

Vorteil: wahrscheinlich kein

Wie gesagt in anderen Antworten, div Aufruf statt / und % am meisten sorgt für wahrscheinlich, dass der Betrieb nur einmal auf Baugruppenebene erfolgt.

Aber in den meisten modernen Kontexten:

  1. Die CPU so gut mathematische Operationen optimiert, dass außer in der innersten Schleife einer Berechnung erfolgt zig Mal, jede Leistung getroffen durch Wiederholung ist wahrscheinlich viel kleiner ist als andere Performance-Hits (wie anderer Code um, Cache-Miss, etc.). => Vorteil von div, falls vorhanden, ist im Allgemeinen vernachlässigbar.
  2. Auch bei Verwendung von / und % machen moderne Compiler das Richtige, indem sie nur einen Divisionsbefehl erzeugen, der den Quotienten und den Rest daraus holt. => Kein tatsächlicher Nutzen für div.

Drawback: div und Freunde binden Sie den Code auf einen bestimmten Typ

Wenn die genaue Art der Zahlen (zB int oder long) sind statisch bekannt (das heißt, Ihr Code explizit verwendet int oder lang immer), mit div für int und ldiv für lange ist in Ordnung.

Aber wenn man die gute Praxis der Programmierung in kleine Teile zu vermeiden unnötige Annahmen folgt, merkt man schnell, dass div und ldiv bindet die Code-Typen int oder long jeweils verwendet wird. Im Gegenteil, / und % passen sich automatisch an den tatsächlich verwendeten Typ an und halten den Code sauberer. Diese

ist besonders sichtbar in zwei Fällen:

  • Sie typedef zu abstrahieren tatsächlichen Typen verwenden - div ist ungeschickt auch in C!
  • verwenden Sie Vorlagen, um tatsächliche Typen wegzurollen - div schlägt Vorlagen.

Beispiel

Das folgende Beispiel zeigt, dass Code mit ‚/‘ und ‚%‘ ist sauber, einfach und nicht in int, lang gebunden, lange, lange oder was auch immer, während Code div und Freunde werden ungeschickt.

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. 

Quellcode:

#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

Okay, das ist schon älter, aber ich hier nur gestolpert. Der wichtigste Unterschied ist hier: Das Ergebnis von div() ist definiert. Der C-Standard sagt nicht, wie der Quotient abgerundet wird. Das liegt daran, dass der Compiler in der Lage sein sollte, die Implementierung der Maschine zu verwenden, die von der CPU abhängt. Zwei verschiedene Implementierungen existieren: - Rundung in Richtung -Unendlichkeit - Rundung in Richtung 0 .

div(), ist jedoch angegeben, um Letzteres zu tun, und ist daher tragbar.

Verwandte Themen