2015-10-19 41 views
6

Quindi ho considerato il doppio virgola mobile IEEE754. (Il mio compilatore C++ usa quel tipo per un double).Come viene definito ++ su un grande punto mobile

considerare questo frammento:

// 9007199254740992 is the 53rd power of 2. 
// 590295810358705700000 is the 69th power of 2. 

for (double f = 9007199254740992; f <= 590295810358705700000; ++f){ 
    /* what is f?*/ 
} 

Presumibilmente f incrementi in anche i passaggi fino alla potenza di 54 ° 2, a causa di arrotondamenti?

Quindi dopo, non succede nulla a causa dell'arrotondamento?

È corretto? È anche ben definito?

+1

Spiacente, chiarito la domanda. –

+3

Le cose si fanno strane con punti fluttuanti grandi/piccoli. I numeri in virgola mobile hanno una precisione limitata. Per numeri estremamente grandi, '1' potrebbe anche essere un errore di arrotondamento.Per numeri estremamente piccoli, '1' è così grande che potrebbe" annegare "altre cifre. –

+4

L'operatore '++' per 'double' è definito in modo che' ++ f' è equivalente a 'f + = double (1)'. – tmlen

risposta

1

Essendo f ++ uguale a f = f + 1, come indicato nei commenti, e come ho provato a me stesso, f == f + 1 (!!) per un grande f dipendente dalla piattaforma. Una spiegazione è qui (per i piccoli numeri, ma il principio è lo stesso) http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BinMath/addFloat.html

Ecco come aggiungere numeri in virgola mobile.

Innanzitutto, convertire le due rappresentazioni in notazione scientifica. Quindi, rappresentiamo esplicitamente il nascosto 1. Per aggiungere, abbiamo bisogno che gli esponenti dei due numeri siano gli stessi. Lo facciamo riscrivendo Y. Ciò comporterà che Y non è normalizzato, ma il valore è equivalente a a Y normalizzato. Aggiungi l'esponente x - y a Y. Spostare il punto di radiazione della mantissa (segno) Y lasciato da x - y per compensare il cambio di esponente . Aggiungi le due mantisse di X e Y regolate insieme. Se la somma del passaggio precedente non ha un singolo bit di valore 1, a sinistra del punto di radix, quindi regolare il punto di radiazione e l'esponente fino a quando non lo fa. Convertire la rappresentazione in virgola mobile a un byte.

Nel processo di conversione del numero allo stesso esponente, a causa di precisione, 1 viene arrotondato a 0, e quindi f == f + 1.

Secondo IEEE754, dopo la somma il numero viene arrotondato per corrispondere al doppio formato e, a causa dell'operazione di arrotondamento, f == f + 1.

4

++f è essenzialmente uguale a f = f + 1, ignorando il fatto che ++f è un'espressione che restituisce un valore.

Ora, per i valori in virgola mobile, entra in gioco il problema della rappresentabilità. Potrebbe essere che f + 1 non sia rappresentabile. In tal caso, f + 1 valuterà il valore rappresentativo più vicino al valore reale di f + 1. Nel caso in cui ci siano due candidati ugualmente vicini per il valore rappresentabile più vicino, viene usato il round to even.

Questo è coperto nella Operations section of What Every Computer Scientist Should Know About Floating-Point Arithmetic:

Lo standard IEEE richiede che il risultato di addizione, sottrazione, moltiplicazione e divisione essere esattamente arrotondato. Cioè, il risultato deve essere calcolato esattamente e quindi arrotondato al numero a virgola mobile più vicino (usando round to even).

Quindi, se il vostro esempio, per sufficientemente grandi valori di f, troverete che f == f + 1.

+0

Penso che la tua risposta non sia corretta. qui non è la rappresentabilità che entra in gioco, ma l'operazione di aggiunta. Infatti 1 è rappresentabile, f è rappresentabile (nell'ambiente l'ho testato), il risultato è un doppio ed è assegnato a un doppio. – AndrewBloom

+0

@AndrewBloom E 'nel risultato dell'operazione che entra in gioco la rappresentabilità. Quando hai 'x + y', ovviamente' x' e 'y' sono rappresentabili. La domanda è se il vero valore di 'x + y' sia o meno rappresentabile. Quando non lo è, lo standard IEEE754 decreta che l'espressione restituisce il valore rappresentativo più vicino al valore vero. A mio parere, la descrizione della tua risposta su come l'aritmetica è implementata secondo IEEE754 non è accurata. –

+0

ha detto che sembra che nella CPU si faccia l'aggiunta e si abbia un registro con il valore aritmetico vero come risultato, e convertirlo in un doppio dopo. Non penso che questo accada però. – AndrewBloom

1

Sì, questo ciclo non finirà mai con il problema dell'arrotondamento. Spero che il motivo sia chiaro per voi (dal momento che avete familiarità con https://en.wikipedia.org/wiki/IEEE_floating_point) ma lasciatemelo descrivere in breve per il pubblico impaziente.

Possiamo pensare al punto mobile come forzato dal compilatore/FPU/presentazione speciale standard del numero. Ad esempio di recensione semplice li ha lasciati:

  • 2E4
  • 0.2e5

Entrambe le tre forme rappresenta lo stesso numero. Le ultime due forme si chiamano "scienza" ma qual è il migliore? Le risposte IEEE754 - l'ultima perché possiamo salvare lo spazio omettendo il numero e scrivere semplicemente .2e5. Tale analogia decimale è molto vicina alla presentazione binaria dove c'è uno spazio per la mantissa (.2) ed esponente (5).

Ora facciamo lo stesso per 20000,00000000001

0.2000000000000001e5

Come possiamo vedere mantissa crescita e v'è un certo limite in cui memoria fissa traboccherà. Invece di eccezione, sacrifichiamo la precisione, che (a titolo di esempio) si dà come 0.2e5.

Per i numeri più grandi (come in questione) abbiamo perso anche in precisione.

9007199254740992 può essere presentato come 0.9e16 E quando viene aggiunto 1, non accade nulla. Così f = f + 1 crea loop infinito

0

Non so se ci sono problemi in cui loop su grandi valori decimali per incrementi di 1 è una soluzione significativa, ma la gente può essere inciampare su questa questione alla ricerca di una soluzione per la loro infinita ciclo continuo. Pertanto, anche se la domanda chiede solo in che modo l'aggiunta è definita dallo standard, proporrò una soluzione alternativa.

In effetti, per valori elevati di f, f++ == f è true e l'utilizzo di tale valore come l'incremento nel ciclo avrà un comportamento non definito.

Supponendo che sia OK che f essere incrementato di un numero che è il più piccolo numero e superiore 1 per cui il punto mobile ha una rappresentazione f + e > f. In tal caso, soluzione riportata di seguito in cui il ciclo sarà sempre terminare potrebbe essere OK:

// use template, or overloads for different floatingpoints 
template<class T> 
T add_s(T l, T r) { 
    T result = l + r; 
    T greater = std::max(l, r); 
    if(result == greater) 
     return std::nextafter(greater, std::numeric_limits<T>::max()); 
    return result; 
} 

// ... 
for (double f = /*...*/; f < /*...*/; f = add_s(f, 1.0)) 

Detto questo, l'aggiunta di piccoli carri a enormi carri allegorici si tradurrà in un cumulo di errori incontrollabile. Se ciò non è giusto per te, allora hai bisogno di matematica di precisione arbitraria, non in virgola mobile.