2011-10-11 13 views
36

Kiedy mnożąc liczbę zmiennoprzecinkową, która jest bardzo zbliżona do 1 z int> 0, to może nigdy być interpretowane jako 1.Czy liczbę 0.99999999999 można zaokrąglić do 1,0 podczas mnożenia?

Oznacza to, że jeśli Math.random() zwraca jego najwyższy wynik (który jest 1 stopień poniżej 1,0) , będzie

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

być 8 lub 7?

Dla praktycznego przykładu, może to często wykorzystywane konstrukt dać indeks z błędem bounds:

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

Jestem szczególnie zainteresowany w odpowiedzi dla Java i ActionScript 3, ale przypuszczam, że wszyscy oni użyć takie same zasady dla arytmetyki zmiennoprzecinkowej i odpowiedzi dla każdej platformy byłyby użyteczne.

Aktualizacja: Chociaż ja już zaakceptowane odpowiedź, bym nadal docenić potwierdzenie, że to nie może się nie udać w ActionScript 3 albo, ponieważ zgłoszenie kolega, który widział go pomylić raz jest to, co skłoniło mnie do częściowo zadaj to pytanie.

+0

Byłbym bardzo zaskoczony, gdyby taki numer * nigdy * przelewał do następnego int ... ale będę czekać na lepsza odpowiedź ... – bdares

+4

@UdoFholl Nie zamierzałem używać żadnej oficjalnej notacji matematycznej, chciałem tylko wskazać liczbę z wieloma wieloma dziewięcioma. –

+0

* Podczas pomnożenia liczby zmiennoprzecinkowej, która jest bardzo zbliżona do 1, można ją zinterpretować jako 1 * - Tak, jeśli pomnożysz ją przez 0 ;-) JK – aioobe

Odpowiedz

44

Jeśli pomnożysz największą wartość poniżej 1,0 za pomocą someInt (> 0), wynik nigdy nie będzie someInt.

To może być wyczerpująco przetestowane dla liczb tak:

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); 

Fragment produkuje żadnego wyjścia.

(Math.ulp zwraca odległość pomiędzy dana podwójne i podwójną wartość następnego większe wielkości. Twierdzenie, zapewnia tym samym, że greatestLessThanOne rzeczywiście jest największą wartością mniejszą niż 1,0).

Innymi słowy, linia

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

nigdy nie spowoduje wyjątku ArrayIndexOutOfBoundsException.


Ponadto według Mark Dickinsons komentarzu nad here ten posiada również podczas mnożenia z podwójnym.

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

Dobra odpowiedź. I [Java] (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html) używa polecenia round-to-nearest: "Język programowania Java wymaga, aby arytmetyka zmiennoprzecinkowa zachowywała się jak gdyby każdy operator zmiennoprzecinkowy zaokrąglał wynik zmiennoprzecinkowy do dokładności wyniku, Nieokreślone wyniki muszą być zaokrąglone do reprezentowalnej wartości najbliższej nieskończenie dokładnemu wynikowi, jeżeli dwie najbliższe reprezentowalne wartości są jednakowo bliskie, to z najmniej znaczącym bitem zerowym został wybrany domyślny tryb zaokrąglania standardu IEEE 754 znany jako ** round to nearest **. " –

+1

dzięki za doskonałą lekcję z języka Java +1 – mKorbel

+0

Zaakceptowano tę odpowiedź, ponieważ większość platform będzie postępować zgodnie z IEEE 754. Czy jednak wiesz, czy ActionScript 3/Flash również to robi? Słyszałem pogłoski, że w rzeczywistości może się tam nie udać. –

-2

tuż to może być tak:

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

BigDecimal'? Jest to dość kosztowna "poprawka" (jeśli w rzeczywistości jest potrzebna) dla prostej instrukcji, która może być wykonywana zbyt często. –

+1

To nie tylko kosztowne, ale także problem statystycznych charakterystyk rozkładu losowego. – leftaroundabout

+0

nie, mam na myśli to tylko przykład, możesz użyć innego typu obiektu mniejszego niż bigdecimal –

Powiązane problemy