Perché la divisione intera ha come risultato un numero intero?

4

Abbiamo imparato in Introduzione alla Programmazione che se dividi due interi, ottieni sempre un numero intero. Per risolvere il problema, rendi almeno uno di questi numeri interi a virgola mobile.

Perché il compilatore non capisce che voglio che il risultato sia un numero decimale?

    
posta moonman239 21.01.2016 - 21:18
fonte

4 risposte

13

Why doesn't the compiler understand I want the result to be a decimal number?

Il compilatore C ++ sta semplicemente seguendo regole ben definite e deterministiche come stabilito nello standard C ++ . Lo standard C ++ ha queste regole perché il comitato per gli standard ha deciso di farlo in questo modo.

Potrebbero aver scritto lo standard per dire che la matematica intera ha come risultato numeri in virgola mobile, o lo fa solo nel caso di un resto. Tuttavia, ciò aggiunge complessità: o ho bisogno di sapere in anticipo quale sia il risultato, o magari riconvertirlo in intero se dà sempre un float. Forse io voglio un intero.

Una delle filosofie fondamentali di C ++ è "non paghi per quello che non usi". Se in realtà vuoi la complessità del mixaggio di interi e float (e delle istruzioni aggiuntive della CPU e degli accessi alla memoria che questo comporta 1 ), fai il cast del tipo come hai menzionato nella tua domanda . Altrimenti, attenersi alla matematica standard intera.

Infine, la miscelazione di variabili integrali e in virgola mobile può comportare una perdita di precisione e risultati talvolta errati, come discuto di seguito. Se lo vuoi, allora pagalo: altrimenti, lo standard impone ai compilatori di attenersi a un rigido insieme di regole per mescolare i tipi di dati. Questo è un comportamento ben definito: come sviluppatore C ++, posso osservarlo nello standard e vedere come funziona.

Ci sono essenzialmente tre modi per fare ciò che stai cercando di fare, ognuno con vantaggi e svantaggi.

  • Matematica intera: questo risulta nel troncare i risultati durante la divisione come hai scoperto. Se si desidera la parte decimale, è necessario trattarla separatamente dividendo, ottenendo il resto e trattando la parte decimale come il resto diviso per il divisore. Questo è un po 'più complesso di un'operazione e ha più variabili da manipolare.

  • Calcolo matematico a virgola mobile: questo in genere produce risultati (sufficienti) corretti per valori piccoli, ma può facilmente introdurre errori con precisione e arrotondamenti, specialmente all'aumentare dell'esponente. Se dividi un numero elevato con un numero piccolo, potresti persino causare un underflow o semplicemente ottenere un risultato errato perché le scale dei numeri non giocano bene tra loro.

  • Fai i tuoi calcoli. Esistono corsi che gestiscono precisione estesa dei numeri decimali e razionali . Questi in genere sono più lenti della matematica sui tipi predefiniti, ma in genere sono ancora piuttosto veloci e forniscono matematica di precisione arbitraria. Arrotondare e altri problemi non sono automatici come lo sono con i float IEEE, ma ottieni più controllo e sicuramente più precisione.

La chiave qui è scegliere in base al dominio del problema. Tutti e tre i metodi di rappresentazione dei numeri hanno i loro vantaggi e svantaggi. Usando un contatore di loop? Scegli un tipo integrale. Rappresentare le posizioni nello spazio 3D? Probabilmente un gruppo di galleggianti. Vuoi tenere traccia dei soldi? Utilizza un tipo decimale fisso.

1 Le architetture CPU più popolari (ad es. x86-64 ) saranno separate insiemi di istruzioni che operano su diversi tipi di registro come intero e virgola mobile, oltre a istruzioni aggiuntive per la conversione tra integrale, virgola mobile e varie rappresentazioni di essi (firmati e non firmati, float e double). Alcune di queste operazioni possono comportare anche l'accesso alla memoria: convertire un valore e memorizzarlo in memoria (la sua variabile). La matematica a livello di CPU non è semplice come "integer in, float out". Mentre aggiungere due interi può essere una semplice operazione molto , probabilmente una singola istruzione, mescolando i tipi di dati può aumentare la complessità.

    
risposta data 21.01.2016 - 21:41
fonte
7

Questo è dovuto all'evoluzione dell'hardware. Nei primi tempi dei computer, non tutte le macchine avevano un'unità a virgola mobile, l'hardware semplicemente non era in grado di comprendere la nozione di un numero in virgola mobile. Ovviamente, i numeri in virgola mobile possono essere implementati come un'astrazione software, ma ciò ha notevoli svantaggi. Tutta l'aritmetica su queste macchine doveva essere pura aritmetica per impostazione predefinita.

E ancora oggi esiste una netta distinzione tra unità aritmetiche integer e floating point all'interno di una CPU. I loro operandi sono memorizzati in file di registro separati per iniziare, e un'unità intera è cablata per prendere due argomenti interi e produrre un risultato intero che finisce in un registro intero. Alcune CPU richiedono persino un valore intero per essere memorizzate in memoria e quindi ricaricate nuovamente in un registro a virgola mobile, prima che possa essere ricodificato in un numero in virgola mobile, prima di poter eseguire una divisione in virgola mobile su di esso.

Come tale, la decisione presa dagli sviluppatori C all'inizio del linguaggio (C ++ ha semplicemente ereditato questo comportamento), era l'unica decisione appropriata da prendere, e rimane di valore oggi: se hai bisogno di matematica in virgola mobile, Puoi usarlo. Se non ne hai bisogno, beh non devi.

    
risposta data 21.01.2016 - 21:44
fonte
1

10/2 con numeri interi ti dà esattamente 5 - la risposta corretta.

Con la matematica in virgola mobile, 10/2 potrebbe dare la risposta corretta *.

In altre parole, è impossibile che i numeri in virgola mobile siano "perfetti" sull'hardware corrente - solo la matematica dei numeri interi può essere corretta, sfortunatamente non è possibile fare cifre decimali, ma ci sono dei semplici accorgimenti.

Ad esempio invece di 4/3, do (4 * 1000) / (3 * 1000) == 1333. Disegna a. nel software quando si visualizza la risposta all'utente (1.333). Questo ti dà una risposta precisa, invece di una errata con un numero di cifre decimali.

Gli errori matematici a virgola mobile possono sommarsi per causare errori significativi: qualsiasi cosa importante (come la finanza) utilizzerà la matematica dei numeri interi.

* l'esempio 10/2 sarà effettivamente corretto con matematica in virgola mobile, ma non puoi fare affidamento su di esso, molti altri numeri danno risultati errati ... per maggiori dettagli leggi: link Il punto è che non puoi fare affidamento sull'accuratezza quando sono coinvolti i punti mobili

    
risposta data 06.03.2016 - 12:53
fonte
0

Sebbene tecnicamente non sia completamente corretto, il C ++ è ancora considerato un superset di C, ne è stato ispirato e in quanto tale si appropria di alcune sue proprietà, essendo la divisione intera una di esse.

C è stato principalmente progettato per essere efficiente e veloce, e gli interi sono generalmente molto più veloci di quelli mobili, perché il tipo intero è legato all'hardware, mentre i punti mobili devono essere calcolati.

Quando l'operando / riceve due interi, uno a sinistra e uno a destra, potrebbe non eseguire nemmeno la divisione, il risultato può essere calcolato usando l'aggiunta semplice e un ciclo, chiedendo quante volte l'operando sul lato destro si inserisce nell'operando a sinistra.

    
risposta data 21.01.2016 - 22:04
fonte

Leggi altre domande sui tag