2011-10-11 15 views
36

Quando moltiplicare un numero a virgola mobile che è molto vicino a 1 int> 0, può mai essere interpretato come 1.È possibile arrotondare a 0,99999999999 a 1,0 quando si moltiplica?

Cioè, se Math.random() restituisce il suo massimo risultato possibile (che è 1 gradino sotto 1.0) ,

(int)(Math.random() * 8) 

essere 8 o 7?

Per un esempio pratico, può questo costrutto spesso usato dare un indice di errore di limiti:

someArray[(int)(Math.random() * someArray.length)]; 

Sono specificamente interessati a risposte per Java e ActionScript 3, ma suppongo che tutti usano il stesse regole per l'aritmetica in virgola mobile e le risposte per qualsiasi piattaforma sarebbero utili.

Aggiornamento: Anche se ho già accettato una risposta, sarei ancora apprezzare la conferma che questo non si può sbagliare in ActionScript 3, giacché la segnalazione di un collega che lo vide andare male una volta che è ciò che in parte mi ha spinto a fai questa domanda

+0

Sarei abbastanza sorpreso se un tale numero * mai * straripato nella prossima int ... ma io ti aspetterò un risposta migliore ... – bdares

+4

@UdoFholl Non intendevo usare alcuna notazione matematica ufficiale, volevo solo indicare un numero con molti molti nove. –

+0

* Quando si moltiplica un numero mobile molto vicino a 1, può mai essere interpretato come 1 * - Sì, se lo si moltiplica per 0 ;-) JK – aioobe

risposta

44

Se si moltiplica il valore massimo inferiore a 1,0 con someInt (> 0), il risultato non sarà mai someInt.

Questo può essere esaustivamente testati per gli interi in questo modo:

Double greatestLessThanOne = Double.longBitsToDouble(4607182418800017407L); 

// Assert that greatestLessThanOne is indeed the largest double less than 1. 
//assert 1.0 == greatestLessThanOne + Math.ulp(greatestLessThanOne); 

for (int i = 1; i >= 0; i++) 
    if ((int) (greatestLessThanOne * i) == i) 
     System.out.println("Exception found: " + i); 

Il frammento non produce alcun output.

(Math.ulp restituisce la distanza tra la proposta doppio e il doppio valore successivo più grande in ampiezza. L'asserzione garantisce così che greatestLessThanOne è effettivamente il massimo valore inferiore a 1,0.)

In altre parole, la linea

Object element = elementArray[(int)(Math.random() * elementArray.length)]; 

non daranno mai origine a ArrayIndexOutOfBoundsException.


Inoltre, secondo Mark Dickinson commento sopra here, questo vale anche quando si moltiplicano con un doppio.

With IEEE 754 floating-point arithmetic in round-to-nearest mode, you can show that x * y < y for any x < 1.0 and any non-tiny positive y . (It can fail if y is either subnormal or the smallest positive normal number.)

+2

Buona risposta. E [Java] (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html) usa round-to-nearest: "Il linguaggio di programmazione Java richiede che l'aritmetica in virgola mobile si comporti come se ogni operatore a virgola mobile ha arrotondato il risultato in virgola mobile alla precisione del risultato, i risultati inesatti devono essere arrotondati al valore rappresentabile più vicino al risultato infinitamente preciso, se i due valori rappresentativi più vicini sono ugualmente vicini, quello con il bit zero meno significativo è scelto. Questa è la modalità di arrotondamento predefinita dello standard IEEE 754 nota come "arrotondato al più vicino **". –

+1

grazie per l'eccellente lezione da Java +1 – mKorbel

+0

Questa risposta è stata accettata poiché la maggior parte delle piattaforme seguirà IEEE 754. Tuttavia, si sa se ActionScript 3/Flash lo fa altrettanto? Ho sentito dire che può davvero andare storto lì. –

-2

proprio dietro di esso, potrebbe essere simile a questo:

BigDecimal bd = new BigDecimal(Double.toString(d)); 
bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP); 
+10

' BigDecimal'? Questa è una "correzione" piuttosto costosa (se è effettivamente necessaria) per una semplice dichiarazione che può essere eseguita troppo spesso per quello. –

+1

Questo non è solo costoso, è anche un problema per le caratteristiche statistiche della distribuzione casuale. – leftaroundabout

+0

no, voglio dire solo un esempio, puoi usare un altro tipo di oggetto più piccolo di bigdecimal –

Problemi correlati