C ++ Der beste Weg, um Ganzzahldivision und Rest zu erhalten


102

Ich frage mich nur, ob ich a durch b teilen möchte und sowohl am Ergebnis c als auch am Rest interessiert bin (z. B. wenn ich die Anzahl der Sekunden habe und diese in Minuten und Sekunden aufteilen möchte), was der beste Weg ist mach es?

Wäre es

int c = (int)a / b;
int d = a % b;

oder

int c = (int)a / b;
int d = a - b * c;

oder

double tmp = a / b;
int c = (int)tmp;
int d = (int)(0.5+(tmp-c)*b);

oder

Vielleicht gibt es eine magische Funktion, die beides gleichzeitig ermöglicht?


5
Alle Antworten unten scheinen vernünftig zu sein. Ich möchte nur hinzufügen, dass jeder Mist mit double(Ihrem letzten Artikel) für mich eine schlechte Idee ist. Sie werden am Ende Zahlen haben, die nicht in einer Reihe stehen und Sie in Leistung und Leistung kosten können Größe der ausführbaren Datei (war für mich auf bestimmten eingebetteten Systemen immer ein Problem).
nhed

2
Die dritte ist eine schlechte Option: Was ist, wenn tmp = 54.999999999999943157? Das heißt, Casting im alten Stil ist niemals eine kluge Sache.
Jimifiki

Antworten:


98

Auf x86 ist der Rest ein Nebenprodukt der Division selbst, sodass jeder halbwegs anständige Compiler ihn nur verwenden kann (und kein erneutes ausführen kann div). Dies wird wahrscheinlich auch auf anderen Architekturen durchgeführt.

Anleitung: DIVsrc

Hinweis: Vorzeichenlose Unterteilung. Teilt den Akku (AX) durch "src". Wenn der Divisor ein Bytewert ist, wird das Ergebnis an AL und der Rest an AH gesetzt . Wenn Divisor ein Wortwert ist, wird DX: AX durch "src" geteilt und das Ergebnis wird in AX gespeichert und der Rest wird in DX gespeichert .

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

9
Ich denke, viele Menschen wissen aus der Grundschule, dass man bei einer Teilung den Rest kostenlos bekommt. Die eigentliche Frage ist: Sind unsere Compiler intelligent genug, um dies zu nutzen?

1
@jdv: Ich wäre nicht überrascht. Es ist eine sehr einfache Optimierung.
Jon Purdy

68
Ich habe einen Schnelltest versucht. Bei g ++ 4.3.2 mit -O2 zeigt die Assembler-Ausgabe dies deutlich mit einer idivlAnweisung und den Ergebnissen in eax und edx. Ich wäre schockiert gewesen, wenn es nicht so gewesen wäre.
Fred Larson

2
@EuriPinhollow Stimmen Sie zu, dass dies keine Frage zu x86 ist. Ich habe es nur als Beispiel gegeben, mit der höchst zweifelhaften Annahme, dass andere Architekturen wahrscheinlich etwas Ähnliches tun.
Cnicutar

3
Sie müssen den Compiler jedoch anweisen, zumindest für g ++ zu optimieren. Ein einfaches Experiment mit g ++ 6.3.0 unter x86_64 ergab, dass Sie ohne Optimierung zwei idivlAnweisungen erhalten, mit -O1oder höher jedoch eine. Wie im Handbuch steht: „Ohne Optimierungsoption… sind Aussagen unabhängig“ .
Tom Zych

79

std::div Gibt eine Struktur mit Ergebnis und Rest zurück.


5
Ich bin gespannt, ob dies tatsächlich effizienter ist als beispielsweise Option 1 auf einem modernen Compiler.
Greg Howell

2
Schön, ich wusste es nicht. Ist es schneller

Nett. Würdest du zufällig wissen, ob man irgendwo lange lange implementiert ist?
Cookie

@Cookie: C ++ 03 hat kein Konzept von long long, aber es ist sehr wahrscheinlich, dass Ihr Compiler eine long longÜberladung std::divals Erweiterung hat.
ildjarn

@ Greg: Ich glaube nicht, da es höchstwahrscheinlich Ergebnisse in eine Struktur im Speicher schreiben und zurückgeben muss, was (es sei denn, es wird als Inline kompiliert und der Strukturzugriff wird optimiert) eine größere Strafe darstellt als dies zu tun eine zusätzliche Teilungsanweisung.
Pezcode

28

Zumindest unter x86 verwendet g ++ 4.6.1 nur IDIVL und erhält beide von dieser einzelnen Anweisung.

C ++ - Code:

void foo(int a, int b, int* c, int* d)
{
  *c = a / b;
  *d = a % b;
}

x86-Code:

__Z3fooiiPiS_:
LFB4:
    movq    %rdx, %r8
    movl    %edi, %edx
    movl    %edi, %eax
    sarl    $31, %edx
    idivl   %esi
    movl    %eax, (%r8)
    movl    %edx, (%rcx)
    ret

Ist die Bestellung wichtig? Wenn Sie beispielsweise über a iterieren, müssen /=Sie möglicherweise eine temporäre Variable verwenden, um die Division zuerst beizubehalten.
AnnanFay

@Annan Damals war es egal und heutzutage auch nicht. Compiler sind intelligent genug, um es neu zu ordnen.
Sahsahae

10

Beispielcode-Test div () und kombinierte Division & Mod. Ich habe diese mit gcc -O3 kompiliert und musste den Aufruf von doNothing hinzufügen, um zu verhindern, dass der Compiler alles optimiert (die Ausgabe wäre 0 für die Division + Mod-Lösung).

Nehmen Sie es mit einem Körnchen Salz:

#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);
}

Ausgänge: 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);
}

Ausgänge: 25



3

Wenn alles andere gleich ist, ist die beste Lösung eine, die Ihre Absicht klar zum Ausdruck bringt. So:

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

ist wahrscheinlich die beste der drei von Ihnen vorgestellten Optionen. Wie in anderen Antworten angegeben, divberechnet die Methode jedoch beide Werte gleichzeitig für Sie.


3

Sie können g ++ 4.6.3 hier mit 64-Bit-Ganzzahlen auf einer 32-Bit-Intel-Plattform nicht vertrauen. a / b wird durch einen Aufruf von divdi3 berechnet und a% b wird durch einen Aufruf von moddi3 berechnet. Ich kann mir sogar ein Beispiel ausdenken, das mit diesen Aufrufen a / b und ab * (a / b) berechnet. Also benutze ich c = a / b und ab * c.

Die div-Methode ruft eine Funktion auf, die die div-Struktur berechnet, aber ein Funktionsaufruf scheint auf Plattformen mit Hardwareunterstützung für den integralen Typ ineffizient zu sein (dh 64-Bit-Ganzzahlen auf 64-Bit-Intel / AMD-Plattformen).


-4

Sie können einen Modul verwenden, um den Rest zu erhalten. Obwohl die Antwort von @ cnicutar sauberer / direkter erscheint.


2
Ja, das Originalposter hat den Moduloperator verwendet. Die Frage ist, wie es effizient gemacht werden kann.
Mark Lakata
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.