In Introduzione alla programmazione abbiamo imparato che se dividi due numeri interi, ottieni sempre un numero intero. Per risolvere il problema, imposta almeno uno di questi numeri interi.
Perché il compilatore non capisce che voglio che il risultato sia un numero decimale?
Commenti
Answer
Perché il compilatore non capisce che voglio che il risultato sia un numero decimale?
Il compilatore C ++ sta semplicemente seguendo regole ben definite e deterministiche come stabilito nello standard C ++. Lo standard C ++ ha queste regole perché comitato per gli standard ha deciso di farlo in questo modo.
Avrebbero potuto scrivere lo standard per dire che la matematica intera risulta in numeri in virgola mobile, o lo fa solo in il caso di un resto. Tuttavia, ciò aggiunge complessità: o ho bisogno di sapere in anticipo qual è il risultato, o forse converto di nuovo in intero se dà sempre un float. Forse voglio un numero intero .
Una delle filosofie centrali di C ++ è “non paghi per ciò che non usi . ” Se in realtà vuoi la complessità di mescolare numeri interi e float (e le istruzioni extra della CPU e laccesso alla memoria questo comportano 1 ), allora esegui il cast di tipo come hai menzionato nella tua domanda . Altrimenti, attenersi alla matematica dei numeri interi standard.
Infine, mescolare variabili integrali e in virgola mobile può comportare una perdita di precisione e talvolta risultati errati come discusso di seguito. Se vuoi questo, allora pagalo: altrimenti, lo standard impone che i compilatori si attengano a un rigido insieme di regole per mescolare i tipi di dati. Questo è un comportamento ben definito: come sviluppatore C ++, posso cercarlo nello standard e vedere come funziona.
Ci sono essenzialmente tre modi per fare ciò che stai cercando di fare, ciascuno con vantaggi e svantaggi.
-
Matematica intera: questo si traduce in un troncamento dei risultati durante la divisione come hai scoperto. Se vuoi la parte decimale, devi trattarla separatamente dividendo, ottenendo il resto e trattando la parte decimale come il resto diviso per il divisore. Questa operazione è un po più complessa e ha più variabili con cui destreggiarsi.
-
Matematica in virgola mobile: generalmente produrrà risultati corretti (sufficienti) per valori piccoli, ma può introdurre facilmente errori con precisione e arrotondamento, soprattutto allaumentare dellesponente. Se dividi un numero grande per un numero piccolo, potresti persino causare un underflow o semplicemente ottenere un risultato sbagliato perché le scale dei numeri non funzionano bene luna con laltra.
-
Fai i tuoi calcoli. Esistono classi che gestiscono la precisione estesa di numeri decimali e razionali . Questi saranno in genere più lenti della matematica sui tipi incorporati, ma in genere sono ancora piuttosto veloci e forniscono calcoli di precisione arbitraria. Larrotondamento e altri problemi non sono automatici come 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 propri vantaggi e svantaggi. Usi un contatore di loop? Scegli un tipo integrale. Rappresentare luoghi nello spazio 3D? Probabilmente un gruppo di carri allegorici. Vuoi tenere traccia del denaro? Utilizza un tipo decimale fisso.
1 Architetture CPU più diffuse (ad es. x86-64 ) avrà set separati di istruzioni che operano su diversi tipi di registro come intero e virgola mobile, oltre a istruzioni extra per convertire tra integrale, virgola mobile e varie rappresentazioni di essi (con segno e senza segno, float e doppio). Alcune di queste operazioni possono comportare anche laccesso alla memoria: convertire un valore e memorizzarlo nella memoria (la sua variabile). La matematica a livello di CPU non è semplice come “integer in, float out.”Sebbene laggiunta di due numeri interi possa essere unoperazione molto semplice, forse una singola istruzione, mescolare i tipi di dati può aumentare la complessità.
Commenti
- Dici che lo standard C ++ stabilisce che tale comportamento dovrebbe essere così. Perché? ‘ t renderebbe le cose più facili da dire, ” La divisione di numeri interi che non sono divisibili in modo uniforme si traduce in float, qualsiasi altra divisione è un gioco leale. ”
- @ moonman239 vedi le mie modifiche.
- @ moonman239 Non per autori di compilatori. Molte architetture CPU di uso comune forniscono un risultato intero quando viene chiesto di eseguire la divisione con due numeri interi. Dovrebbero implementare un controllo per risultati non interi e quindi passare a utilizzare il punto mobile più lento In alternativa, avrebbero potuto passare alla divisione in virgola mobile e perdere linteresse di coloro che volevano una matematica veloce, di quelli che volevano una matematica accurata e di coloro che erano abituati a C. non è ‘ t unopzione perché ciò romperebbe la compatibilità con il codice esistente.
- Non che tu lo stia sostenendo come alternativa ma creando il tipo statico di unespressione dipendono dai valori di runtime degli operandi che ‘ non funzionano con il sistema di tipo statico C ++ ‘.
- @ moonman239: avere unoperazione che produce un tipo diverso a seconda dei valori degli operandi è pura follia.
Risposta
Ciò è dovuto allevoluzione dellhardware. Allinizio dei computer, non tutte le macchine avevano ununità a virgola mobile, lhardware semplicemente non era in grado di comprendere la nozione di numero in virgola mobile. Ovviamente, i numeri in virgola mobile possono essere implementati come astrazione del software, ma ciò ha degli svantaggi significativi. Tutta laritmetica su queste macchine doveva essere pura aritmetica intera per impostazione predefinita.
E ancora oggi, cè una netta distinzione tra unità aritmetiche intere e in virgola mobile allinterno di una CPU. I loro operandi sono memorizzati in file di registro separati per iniziare, e ununità intera è cablata per accettare due argomenti interi e produrre un risultato intero che finisce in un registro intero. Alcune CPU richiedono persino che un valore intero venga memorizzato e quindi ricaricato in un registro a virgola mobile, prima che possa essere ricodificato in un numero a virgola mobile, prima di poter eseguire una divisione in virgola mobile su di esso.
In quanto tale, la decisione presa dagli sviluppatori C allinizio del linguaggio (C ++ ha semplicemente ereditato questo comportamento), è stata lunica decisione appropriata da prendere, e rimane di valore oggi: se hai bisogno di matematica in virgola mobile, può usarlo. Se non ne hai bisogno, beh non devi.
Commenti
- È triste che la maggior parte dei vincoli che esistevano al la creazione dello standard C ++ è piuttosto obsoleta oggi! Ad esempio: ” non paghi per ciò che non usi. ” oggigiorno lhardware è dato per scontato e tutto ciò che gli utenti vogliono è esecuzione!
- @ mahen23 Non tutti gli utenti pensano in questo modo. Lavoro in un campo in cui i programmi vengono eseguiti su migliaia di core della CPU in parallelo. In questarea lefficienza è denaro, sia in termini di investimenti che in termini di puro consumo di energia. Un linguaggio come Java non regge il fantasma di una possibilità in quellarea, mentre C ++ sì.
- @ mahen23 No, non è ‘ t – o meglio, è solo se si guardano le attuali architetture della CPU per desktop e superiori. Esistono ancora molti sistemi incorporati che don ‘ to supportano solo parzialmente le operazioni in virgola mobile, e C e C ++ continuano a supportarli al fine di fornire limplementazione più efficiente possibile a meno di utilizzando assembler. A proposito, anche linguaggi di livello superiore come Python distinguono tra operazioni su interi e FP: prova
10 / 3
.
Risposta
10/2 con numeri interi fornisce esattamente 5: la risposta corretta.
Con la matematica in virgola mobile, 10/2 potrebbe dare la risposta corretta risposta *.
In altre parole, è impossibile che i numeri in virgola mobile siano “perfetti” sullhardware corrente – solo la matematica intera può essere corretta, sfortunatamente non può “fare cifre decimali ma ci sono lavori facili arounds.
Ad esempio invece di 4/3, do (4 * 1000) / (3 * 1000) == 1333. Disegna semplicemente a. nel software quando visualizzi la risposta al tuo utente (1.333). Questo ti dà una risposta accurata, invece di una che è errata per un certo numero di cifre decimali.
Gli errori di matematica in virgola mobile possono sommarsi per causare errori significativi: qualsiasi cosa importante (come la finanza) utilizzerà la matematica intera .
* lesempio 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: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Il punto è che “non puoi fare affidamento sulla precisione ogni volta che sono coinvolti
Commenti
- Le implementazioni in virgola mobile conformi a IEEE 754 ti daranno un risultato esatto per 10/2. In effetti, ti daranno esatto risultati per qualsiasi operazione che coinvolge solo operandi interi che hanno un risultato intero a condizione che gli operandi e il risultato possano essere rappresentati esattamente, cosa che possono fare gli interi “abbastanza piccoli”.
- @ 5gon12eder there ‘ non cè bisogno di fare un pignolo, io ‘ sto solo cercando di descrivere un problema complesso in termini semplici. Lintero punto di supporto di valori non interi è quello di avere posizioni decimali ( che può essere fatto usando numeri interi semplicemente moltiplicando tutto per il numero di posizioni decimali che vuoi come ho demsonstraed).
Answer
Sebbene tecnicamente non completamente corretto, C ++ è ancora considerato un superset di C, è stato ispirato da esso e come tale si è appropriato di alcune delle sue proprietà, tra cui la divisione intera.
C è stato progettato principalmente per essere efficiente e veloce, e gli interi sono generalmente molto più veloce dei punti mobili, perché il tipo intero è legato allhardware, mentre i punti mobili devono essere calcolati.
Quando loperando /
riceve due numeri interi, uno del lato sinistro e uno a destra, potrebbe non eseguire nemmeno la divisione, il risultato può essere calcolato utilizzando una semplice addizione e un ciclo, chiedendo quante volte loperando sul lato destro si adatta alloperando sulla sinistra.
//
è loperatore di divisione intera e#
è un commento a riga singola. In Pascal, loperatore di divisione intera è la parola chiavediv
. Entrambi funzionano abbastanza bene per il rispettivo langua ges. C probabilmente fa la cosa peggiore possibile: un operatore che può fare due cose completamente diverse in base a un contesto arbitrario.