sarebbe davvero dipende dal processore, e la gamma di numero intero che è meglio (e usando double
risolverebbe gran parte dei problemi di range)
per le moderne CPU "grandi" come x86-64 e ARM, la divisione intera e la divisione in virgola mobile sono grosso modo lo stesso, e la conversione di un intero in un float o viceversa non è un'attività "difficile" (e fa l'arrotondamento corretto direttamente in quella conversione, almeno), quindi molto probabilmente le operazioni risultanti siamo.
atmp = (float) a;
btmp = (float) b;
resfloat = divide atmp/btmp;
return = to_int_with_rounding(resfloat)
Informazioni su quattro istruzioni della macchina.
D'altra parte, il codice utilizza due divisioni, un modulo e un multiplo, che è molto probabilmente più lungo su tale processore.
tmp = a/b;
tmp1 = a % b;
tmp2 = tmp1 * 2;
tmp3 = tmp2/b;
tmp4 = tmp + tmp3;
Così cinque istruzioni, e tre di questi sono "dividere" (a meno che il compilatore è abbastanza intelligente da riutilizzare a/b
per a % b
- ma è ancora due divisioni distinte).
Ovviamente, se si è al di fuori dell'intervallo di numero di cifre che un float o un double può contenere senza perdere cifre (23 bit per float, 53 bit per doppio), allora il metodo potrebbe essere migliore (supponendo che non ci sia overflow nella matematica intera).
Inoltre, poiché il primo modulo è utilizzato da "tutti", è quello che il compilatore riconosce e può ottimizzare.
Ovviamente, i risultati dipendono sia dal compilatore in uso sia dal processore su cui viene eseguito, ma questi sono i miei risultati dall'esecuzione del codice pubblicato sopra, compilato tramite clang++
(v3.9-pre-release, abbastanza vicino a rilasciato 3.8).
round_divide_by_float_casting(): 32.5 ns
round_divide_by_modulo(): 113 ns
divide_by_quotient_comparison(): 80.4 ns
Tuttavia, la cosa interessante che ho trovato quando guardo il codice generato:
xorps %xmm0, %xmm0
cvtsi2ssl 8016(%rsp,%rbp), %xmm0
xorps %xmm1, %xmm1
cvtsi2ssl 4016(%rsp,%rbp), %xmm1
divss %xmm1, %xmm0
callq roundf
cvttss2si %xmm0, %eax
movl %eax, 16(%rsp,%rbp)
addq $4, %rbp
cmpq $4000, %rbp # imm = 0xFA0
jne .LBB0_7
è che il round
è in realtà una chiamata. Il che mi sorprende, ma spiega perché su alcune macchine (in particolare i processori x86 più recenti), è più veloce.
g++
dà risultati migliori con -ffast-math
, che dà giro:
round_divide_by_float_casting(): 17.6 ns
round_divide_by_modulo(): 43.1 ns
divide_by_quotient_comparison(): 18.5 ns
(questo è con una maggiore conteggio a 100k valori)
Per me il primo è molto più chiaro. So cosa sta cercando di fare. So come sta provando a farlo. Senza correre su carta, non ho idea di cosa stia facendo il secondo. Sto anche ribaltando il primo è più veloce perché il secondo fa diverse operazioni aritmetiche – John3136
Sono d'accordo che la chiarezza è importante. Ma per quanto riguarda la complessità dell'operazione (e la velocità), non ne sono sicuro. –