C ++ Il modo migliore per ottenere la divisione intera e il resto


103

Mi chiedo solo, se voglio dividere a per b, e sono interessato sia al risultato ce al resto (ad esempio, diciamo che ho il numero di secondi e voglio dividerlo in minuti e secondi), qual è il modo migliore per farlo?

Sarebbe

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

o

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

o

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

o

forse c'è una funzione magica che dà entrambi contemporaneamente?


5
tutte le risposte di seguito sembrano ragionevoli, vorrei solo aggiungere che qualsiasi pasticcio con double(il tuo ultimo elemento) mi sembra una cattiva idea, ti ritroverai con numeri che non si allineano e possono costarti in termini di prestazioni e dimensione eseguibile (è sempre stato un problema per me su alcuni sistemi embedded).
nhed

2
La terza è un'opzione BAD: cosa succede se tmp = 54.999999999999943157? Detto questo, il casting vecchio stile non è mai una cosa intelligente da fare.
jimifiki

Risposte:


98

Su x86 il resto è un sottoprodotto della divisione stessa, quindi qualsiasi compilatore decente dovrebbe essere in grado di usarlo (e non eseguirne di divnuovo). Questo è probabilmente fatto anche su altre architetture.

Istruzioni: DIVsrc

Nota: divisione senza segno. Divide l'accumulatore (AX) per "src". Se divisore è un valore in byte, il risultato viene inserito in AL e il resto in AH . Se divisore è un valore di parola, allora DX: AX viene diviso per "src" e il risultato viene memorizzato in AX e il resto viene memorizzato in DX .

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

9
Penso che molte persone sappiano dalla scuola elementare che quando fai una divisione ottieni il resto gratuitamente. La vera domanda è: i nostri compilatori sono abbastanza intelligenti da trarne vantaggio?

1
@jdv: non sarei sorpreso. È un'ottimizzazione molto semplice.
Jon Purdy

68
Ho provato un rapido test. Con g ++ 4.3.2 che usa -O2, l'output dell'assembler lo mostra chiaramente usando idivlun'istruzione e usando i risultati in eax ed edx. Sarei stato scioccato se non fosse stato così.
Fred Larson

2
@EuriPinhollow D'accordo, questa non è una domanda su x86. L'ho fornito solo come esempio, con l'ipotesi altamente dubbiosa che altre architetture probabilmente facciano qualcosa di simile.
cnicutar

3
Tuttavia, è necessario dire al compilatore di ottimizzare, almeno per g ++. Un semplice esperimento con g ++ 6.3.0 su x86_64 ha rivelato che senza ottimizzazione si ottengono due idivlistruzioni, ma con -O1o più si ottiene una. Come dice il manuale, "Senza alcuna opzione di ottimizzazione ... Le dichiarazioni sono indipendenti" .
Tom Zych

80

std::div restituisce una struttura con sia il risultato che il resto.


5
Sono curioso di sapere se questo è effettivamente più efficiente, ad esempio, dell'opzione 1 su un compilatore moderno.
Greg Howell

2
Bello, non lo sapevo. È più veloce?

Bello. Ti capiterà di sapere se uno è implementato per molto tempo da qualche parte?
Cookie

@Cookie: C ++ 03 non ha il concetto di long long, ma è molto probabile che il tuo compilatore abbia un long longsovraccarico std::divcome estensione.
ildjarn

@ Greg: non credo che lo sia, dato che molto probabilmente deve scrivere i risultati in una struttura in memoria e restituirlo, il che (a meno che non compili come inline e l'accesso alla struttura sia ottimizzato) è una penalità maggiore rispetto a farlo un'istruzione di divisione extra.
pezcode

28

Almeno su x86, g ++ 4.6.1 usa solo IDIVL e ottiene entrambi da quella singola istruzione.

Codice C ++:

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

codice x86:

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

L'ordine è importante? Ad esempio, se stai iterando su un /=- potresti dover usare una variabile temporanea per mantenere prima la divisione.
AnnanFay

@Annan Non aveva importanza allora, e non lo è oggi. I compilatori sono abbastanza intelligenti da riordinarlo.
Sahsahae

10

Esempio di codice di test div () e divisione e mod. Li ho compilati con gcc -O3, ho dovuto aggiungere la chiamata a doNothing per impedire al compilatore di ottimizzare tutto (l'output sarebbe 0 per la soluzione division + mod).

Prendilo con un pizzico di sale:

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

Uscite: 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);
}

Uscite: 25



3

A parità di condizioni, la soluzione migliore è quella che esprime chiaramente il tuo intento. Così:

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

è probabilmente la migliore delle tre opzioni che hai presentato. Come notato in altre risposte, tuttavia, il divmetodo calcolerà entrambi i valori contemporaneamente.


3

Non puoi fidarti di g ++ 4.6.3 qui con numeri interi a 64 bit su una piattaforma Intel a 32 bit. a / b è calcolato da una chiamata a divdi3 e a% b è calcolato da una chiamata a moddi3. Posso persino trovare un esempio che calcoli a / be ab * (a / b) con queste chiamate. Quindi uso c = a / be ab * c.

Il metodo div dà una chiamata a una funzione che calcola la struttura div, ma una chiamata di funzione sembra inefficiente su piattaforme che hanno il supporto hardware per il tipo integrale (cioè interi a 64 bit su piattaforme Intel / amd a 64 bit).


-4

Puoi usare un modulo per ottenere il resto. Anche se la risposta di @ cnicutar sembra più pulita / più diretta.


2
Sì, il poster originale utilizzava l'operatore modulo, la domanda è come renderlo efficiente.
Mark Lakata
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.