Perché la divisione di due int non produce il valore corretto quando viene assegnata a double?


110

Come mai nel seguente frammento

int a = 7;
int b = 3;
double c = 0;
c = a / b;

cfinisce per avere il valore 2, anziché 2,3333, come ci si aspetterebbe. Se ae bsono doppi, la risposta va a 2.333. Ma sicuramente perché c già è un doppio avrebbe dovuto funzionare con i numeri interi?

Allora come int/int=doublemai non funziona?


Il possibile duplicato del risultato
phuclv

Risposte:


161

Questo perché stai usando la versione a divisione intera di operator/, che richiede 2 se intrestituisce un int. Per poter utilizzare la doubleversione, che restituisce a double, almeno una delle ints deve essere espressamente castata in a double.

c = a/(double)b;

9
Io preferirei di convertire in modo esplicito sia ae bper doublesemplicemente per chiarezza, ma in realtà non importa.
John Dibling,

31
Poiché la domanda è contrassegnata con C ++, preferirei vedere static_cast <> piuttosto che un cast in C.
Martin York,

16
Personalmente, ritengo che i cast in stile C siano più chiari (il casting nella maggior parte degli altri linguaggi comuni è fatto nel modo in stile C). static_cast<>mi è sempre sembrato lungo senza fiato. Nel caso di primitive, non c'è davvero alcun pericolo di ottenere static_cast<>e reinterpret_cast<>su misto.
Chad La Guardia,

6
@ Tux-D: per calchi aritmetici? Preferirei evitare static_castin questo caso e utilizzare invece il cast in stile C. Non c'è alcun vantaggio nell'usare i cast in stile C ++ qui e ingombrano il codice molto più dei cast in stile C. Il cast aritmetico è esattamente il contesto in cui i cast in stile C sono perfettamente appropriati e in realtà più appropriati di altri cast.
AnT

19
A volte puoi superare in astuzia la gente "senza cast in stile C" scrivendo double(b). Non sempre si rendono conto che si tratta di una conversione, poiché ha lo stesso aspetto di una chiamata esplicita al costruttore.
Steve Jessop,

12

Ecco qui:

a) La divisione di due ints esegue sempre la divisione intera. Quindi il risultato di a/bnel tuo caso può essere solo un file int.

Se vuoi mantenere ae bcome ints, ma dividerli completamente, devi lanciarne almeno uno per raddoppiare: (double)a/bo a/(double)bo (double)a/(double)b.

b) cè a double, quindi può accettare un intvalore in cessione: il intviene automaticamente convertito doublee assegnato a c.

c) Ricorda che nell'assegnazione, l'espressione a destra di =viene calcolata prima (secondo la regola (a) sopra, e senza riguardo alla variabile a sinistra di =) e poi assegnata alla variabile a sinistra di =(secondo ( b) sopra). Credo che questo completi il ​​quadro.


11

Con pochissime eccezioni (posso pensarne solo una), C ++ determina l'intero significato di un'espressione (o sottoespressione) dall'espressione stessa. Quello che fai con i risultati dell'espressione non ha importanza. Nel tuo caso, nell'espressione a / b, non c'è doublein vista; tutto è int. Quindi il compilatore usa la divisione intera. Solo una volta ottenuto il risultato, considera cosa farne e lo converte in double.


3
L'unica eccezione a cui posso pensare è la scelta di un sovraccarico di funzione quando si prende un puntatore: il valore di &funcnamedipende dal tipo a cui lo si esegue .
Steve Jessop,

2
@ Steve Jessop Questa è anche l'unica eccezione a cui riesco a pensare. (Ma date le dimensioni e la complessità dello standard, non vorrei giurare di non averne perso nessuno.)
James Kanze

6

cè una doublevariabile, ma il valore che le viene assegnato è unint valore perché risulta dalla divisione di due ints, che dà "divisione intera" (eliminando il resto). Quindi quello che succede nella linea c=a/bè

  1. a/b viene valutato, creando un temporaneo di tipo int
  2. il valore del temporaneo è assegnato a c dopo la conversione in tipo double.

Il valore di a/bè determinato senza riferimento al suo contesto (assegnazione a double).


6

Quando dividi due numeri interi, il risultato sarà un numero intero, indipendentemente dal fatto che lo memorizzi in un doppio.


5

Nel linguaggio C ++ il risultato della sottoespressione non è mai influenzato dal contesto circostante (con alcune rare eccezioni). Questo è uno dei principi che la lingua segue attentamente. L'espressione c = a / bcontiene una sottoespressione indipendente a / b, che viene interpretata indipendentemente da qualsiasi cosa al di fuori di quella sottoespressione. Alla lingua non interessa che in seguito assegnerai il risultato a un file double.a / bè una divisione intera. Qualsiasi altra cosa non ha importanza. Vedrai questo principio seguito in molti angoli della specifica del linguaggio. Ecco come funzionano C ++ (e C).

Un esempio di un'eccezione che ho menzionato sopra è l'assegnazione / inizializzazione del puntatore a funzione in situazioni con sovraccarico di funzioni

void foo(int);
void foo(double);

void (*p)(double) = &foo; // automatically selects `foo(fouble)`

Questo è un contesto in cui il lato sinistro di un'assegnazione / inizializzazione influenza il comportamento del lato destro. (Inoltre, l'inizializzazione del riferimento all'array impedisce il decadimento del tipo di array, che è un altro esempio di comportamento simile.) In tutti gli altri casi il lato destro ignora completamente il lato sinistro.


4

L' /operatore può essere utilizzato per la divisione di numeri interi o la divisione in virgola mobile. Gli stai dando due operandi interi, quindi sta eseguendo una divisione intera e quindi il risultato viene memorizzato in un double.


2

Questo è tecnicamente un linguaggio dipendente, ma quasi tutte le lingue trattano questo argomento allo stesso modo. Quando c'è una mancata corrispondenza del tipo tra due tipi di dati in un'espressione, la maggior parte delle lingue proverà a trasmettere i dati su un lato del= per abbinare i dati sull'altro lato in base a un insieme di regole predefinite.

Quando si dividono due numeri dello stesso tipo (interi, doppi, ecc.) Il risultato sarà sempre dello stesso tipo (quindi 'int / int' risulterà sempre in int).

In questo caso hai double var = integer result che converte il risultato intero in un doppio dopo il calcolo, nel qual caso i dati frazionari sono già persi. (la maggior parte delle lingue eseguirà questo casting per evitare imprecisioni di tipo senza generare un'eccezione o un errore).

Se desideri mantenere il risultato come doppio, vorrai creare una situazione in cui hai double var = double result

Il modo più semplice per farlo è forzare l'espressione sul lato destro di un'equazione a raddoppiare:

c = a/(double)b

La divisione tra un numero intero e un double risulterà nel cast dell'intero nel double (si noti che quando si fa la matematica, il compilatore spesso "esegue l'upcast" al tipo di dati più specifico per evitare la perdita di dati).

Dopo l'upcast, afinirà come un doppio e ora hai la divisione tra due doppi. Questo creerà la divisione e l'assegnazione desiderati.

ANCORA, tieni presente che questo è specifico del linguaggio (e può anche essere specifico del compilatore), tuttavia quasi tutti i linguaggi (certamente tutti quelli a cui riesco a pensare fuori dalla mia testa) trattano questo esempio in modo identico.


Questa domanda è etichettata [C ++] e lo standard C ++ determina esattamente come funziona. Non sono sicuro di cosa intendi per "linguaggio specifico" e certamente non è specifico del compilatore, assumendo che non siano attivate estensioni del compilatore.
John Dibling,

Inoltre non è corretto dire che "double var = integer result che converte la double var fino a int". Il double non viene lanciato su un int. Il risultato int viene convertito in un double.
John Dibling,

Stavo consentendo la possibilità di estensioni del compilatore (in realtà ho avuto questo problema una volta in cui il mio ambiente stava "trasmettendo in modo errato" i risultati e non riuscivo a capire perché). E il risultato è specifico della lingua poiché in alcune lingue non seguono le stesse regole di casting. Non ho considerato che fosse un tag specifico del C ++. Hai ragione sul commento "double var = integer result". Modificato per riflettere questo. Grazie!
matthewdunnam

0

L'importante è che uno degli elementi di calcolo sia di tipo float-double. Quindi per ottenere un doppio risultato è necessario lanciare questo elemento come mostrato di seguito:

c = static_cast<double>(a) / b;

oppure c = a / static_cast (b);

Oppure puoi crearlo direttamente:

c = 7.0 / 3;

Notare che uno degli elementi di calcolo deve avere '.0' per indicare una divisione di un tipo float-double per un numero intero. Altrimenti, nonostante la variabile c sia double, anche il risultato sarà zero.


Cosa porta la tua risposta che nessuna delle altre 9 risposte non sia già presente?
bolov
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.