Warum div oder ldiv in C/C++ verwenden?

Lesezeit: 7 Minuten

Gibt es einen bestimmten Grund, ldiv oder div anstelle von „https://stackoverflow.com/“ oder „%“ zu verwenden, um zwei Variablen zu dividieren/modulieren?

  • Mögliches Duplikat von Was ist der Zweck der Bibliotheksfunktion div()?

    – mja

    10. November 2018 um 3:09 Uhr

Ja. C99 §7.20.6.2/2 sagt:

Das div, ldivund lldivFunktionen berechnen numer / denom und numer %
denom
in einem einzigen Arbeitsgang.

Benutzeravatar von Jonathan Wood
Jonathan Holz

Die Idee ist, dass die Ergebnisse von / und % von einem einzigen DIV-Befehl auf dem Prozessor bestimmt werden können. Historisch gesehen wird also div() verwendet, um einen optimierten Weg zu bieten, beides zu erhalten.

Ich habe jedoch festgestellt, dass neuere Compiler sowieso in der Lage sind, eine /- und %-Operation in eine einzige Division zu optimieren. Ich habe diese Optimierung zum Beispiel bei Microsoft Visual C++ gesehen. In diesen Fällen bietet div() wirklich keinen Vorteil und kann sogar langsamer sein, wenn es sich um einen Anruf handelt.

  • +1 für eine explizite don't even bother with that, your compiler's probably smarter than that anyway. Jetzt gehe ich einfach und füge meiner Antwort Kursivschrift hinzu;)

    – Frédéric Hamidi

    30. Dezember 2010 um 18:59 Uhr


  • Ich würde denken, dass jeder Compiler, der intelligent genug ist, um aufeinanderfolgende %- und /-Operationen in eine einzelne Prozessoranweisung zu optimieren, auch intelligent genug ist, um einen Aufruf von div() einzufügen. :/

    – Karl Knechtel

    30. Dezember 2010 um 19:37 Uhr

  • Es sei denn, Sie setzen eine Compiler-Option auf nicht Inline-Standardbibliotheksaufrufe. Warum glaube ich, dass es eine solche Option gibt … vielleicht aus der Zeit, als ich noch in C programmiert habe?

    – Präsident James K. Polk

    30. Dezember 2010 um 19:40 Uhr

  • @Karl: Nur weil ein Compiler intelligent genug ist, um einen Aufruf einzufügen, bedeutet das nicht unbedingt, dass er konfiguriert ist oder sogar in allen Fällen dazu in der Lage ist. Jedenfalls hatte ich geglaubt, das mit “darf” und “wenn” abgedeckt zu haben.

    – Jonathan Holz

    30. Dezember 2010 um 22:18 Uhr

Benutzeravatar von Frédéric Hamidi
Frédéric Hamidi

Das ist soll schneller zu sein als mit dem / und % Operatoren, wenn Sie sowohl den Quotienten als auch den Rest gleichzeitig berechnen möchten.

  • édéric: Wenn du sagst “soll schneller zu sein”, bedeutet das, dass es manchmal nicht so ist?

    – Stuart Golodetz

    30. Dezember 2010 um 19:05 Uhr

  • @Stuart, absolut, siehe die Antwort von @Jonathan Wood und meinen nachfolgenden Kommentar. Grundsätzlich, wenn Sie erwägen, die ldiv Familie von Funktionen, um die Leistung zu steigern, denken Sie, dass Ihr Compiler nicht schlau genug ist, um Ihre zu optimieren quotient+remainder Berechnung mit etwas billiger als ein Funktionsaufruf, oder Sie erwarten das ldiv und ihresgleichen sind Compiler-Intrinsics und keine eigentlichen Funktionen.

    – Frédéric Hamidi

    30. Dezember 2010 um 19:09 Uhr


  • @Stuart Golodetz: Das Vorschreiben, dass X schneller sein muss als Y, ist dasselbe wie das Vorschreiben, dass Y sein muss Langsamer als X, was kein gutes Geschäft ist. Die einzige Möglichkeit, sicherzustellen, dass es schneller ist, wäre, dem Optimierer zu verbieten, Code zu sehen, der beides berechnet / und %daran zu hindern, Optimierungen zu verwenden, in denen die Implementierung verwendet wird ldiv. Aber warum sollte sich ein Compiler so einschränken?

    – Steve Jessop

    30. Dezember 2010 um 19:29 Uhr


  • édéric, @Steve: Danke an beide, macht Sinn 🙂

    – Stuart Golodetz

    30. Dezember 2010 um 22:40 Uhr

Kurze Antwort: nicht wirklich in einer modernen Umgebung.

Die Leute haben erklärt, warum davon profitieren div ist schwach oder gar nicht vorhanden.
Es ist eigentlich noch schlimmer: div und Freunde führen eine Typkopplung ein, was der guten Praxis schadet (siehe Abschnitt über Nachteile unten).

Vorteil: wahrscheinlich keiner

Wie in anderen Antworten gesagt, anrufen div Anstatt von / und % stellt höchstwahrscheinlich sicher, dass die Operation nur einmal auf Baugruppenebene durchgeführt wird.

Aber in den meisten modernen Kontexten:

  1. Die CPU hat mathematische Operationen so gut optimiert, dass außer in der innersten Schleife einer Berechnung, die zigfach durchgeführt wurde, jede Leistungseinbuße durch die Wiederholung der Operation wahrscheinlich viel kleiner ist als andere Leistungseinbußen (wie anderer Code in der Nähe, Cache-Fehler usw.). => Nutzen von divfalls vorhanden, ist im Allgemeinen vernachlässigbar.
  2. Auch beim Benutzen / und % Moderne Compiler machen sowieso das Richtige, indem sie nur einen Divisionsbefehl generieren, der Quotient und Rest daraus holt. => Kein tatsächlicher Nutzen für div.

Nachteil: div und Freunde binden Ihren Code an einen bestimmten Typ

Wenn der genaue Typ der Nummern (z int oder long) statisch bekannt sind (d. h. Ihr Code verwendet explizit immer int oder long), using div zum int und ldiv lange ist ok.

Aber wenn Sie der guten Praxis folgen, in kleinen Teilen zu programmieren und unnötige Annahmen zu vermeiden, werden Sie dies schnell feststellen div und ldiv bindet den Code an Typen int oder long beziehungsweise. Andererseits, / und % passt sich automatisch an den Typ an, der im vorliegenden Fall tatsächlich verwendet wird, wodurch der Code sauberer bleibt.

Dies wird besonders in zwei Fällen sichtbar:

  • Sie nutzen typedef tatsächliche Typen abstrahieren — div ist sogar in C ungeschickt!
  • Sie verwenden Vorlagen, um tatsächliche Typen zu abstrahieren — div besiegt Vorlagen.

Beispiel

Das folgende Beispiel zeigt, dass Code, der „https://stackoverflow.com/“ und „%“ verwendet, sauber, einfach und nicht an int, long, long long oder was auch immer gebunden ist, während Code using 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>

// "https://stackoverflow.com/" 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 );
}

Okay, das ist älter, aber ich bin gerade hierher gestolpert. Der wichtigste Unterschied hierbei ist: Das Ergebnis von div() ist definiert. Der C-Standard sagt nicht, wie der Quotient zu runden ist. Das liegt daran, dass der Compiler in der Lage sein sollte, die Implementierung der Maschine zu verwenden, die von der CPU abhängt. Es gibt zwei verschiedene Implementierungen: – Rundung gegen -unendlich – Rundung gegen 0 .

div() ist jedoch für Letzteres spezifiziert und daher portabel.

  • Würdest du einen Link geben, um das zu beweisen?

    – mja

    9. November 2018 um 14:09 Uhr

  • Seit C99: “Wenn ganze Zahlen dividiert werden, ist das Ergebnis des Operators / der algebraische Quotient, wobei jeder Bruchteil verworfen wird”.

    – chux – Wiedereinsetzung von Monica

    12. Januar 2019 um 17:48 Uhr

  • Würdest du einen Link geben, um das zu beweisen?

    – mja

    9. November 2018 um 14:09 Uhr

  • Seit C99: “Wenn ganze Zahlen dividiert werden, ist das Ergebnis des Operators / der algebraische Quotient, wobei jeder Bruchteil verworfen wird”.

    – chux – Wiedereinsetzung von Monica

    12. Januar 2019 um 17:48 Uhr

1387560cookie-checkWarum div oder ldiv in C/C++ verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy