2012-04-09 9 views
20

Jeśli pracuję z wersją double i konwertuję ją na float, jak to działa? Czy wartość zostanie obcięta, aby zmieściła się w float? Czy wartość jest zaokrąglana inaczej? Przepraszam, jeśli to brzmi trochę zaradczo, ale staram się zrozumieć koncepcję konwersji float i double.Konwersja z wersji podwójnej na zmienną w języku Java

+0

http://stackoverflow.com/a/2781125/986169 – giorashc

Odpowiedz

19

Z Java Language Specification, section 5.1.3:

zwężenie prymitywna konwersja z double float jest regulowane przez IEEE 754 zasad zaokrąglania (§4.2.4). Konwersja ta może stracić precyzję, ale również stracić zasięg, co powoduje, że wartość zerowa z niezerowego podwójnego i nieskończoności swobodnej z skończonego podwójnego. Podwójny NaN jest konwertowany na pływający NaN, a podwójna nieskończoność jest konwertowana na nieskończoną sygnaturę o tym samym znaku.

i section 4.2.4 mówi:

Język programowania Java wymaga tego arytmetyki zmiennoprzecinkowej zachowują się tak, jakby każdy operator zmiennoprzecinkową zaokrągleniu jej wynik zmiennoprzecinkową dokładność wyników. Niepoprawne 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 blisko, wybierana jest ta z najmniej znaczącym bitem zerowym. Jest to domyślny tryb zaokrąglania standardu IEEE 754, znany jako round to nearest.

+0

Dzięki za to. Zauważyłem, że wspomina o zastosowaniu rundy IEEE 754 do najbliższego. Czy mimo to można określić inny tryb zaokrąglania? – Franklin

+0

@Franklin: Jest klasa ["RoundingMode'] (http://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html), ale myślę, że dotyczy tylko" BigDecimal " oraz operacje "BigInteger", a nie operacje na elementach pierwotnych. Ale nie jestem w 100% pewny tego. –

+1

Java obsługuje tylko jeden tryb zaokrąglania fp - mówiono o dodawaniu więcej lat temu (głównie dla społeczności HPC, tj. Także o obsłudze denormów itp.), Ale niestety nigdzie się to nie udawało. – Voo

8

Sugerowałbym, że typy zmiennoprzecinkowe są najkorzystniej uważane za reprezentujące zakresy wartości. Powodem, że 0,1f jest wyświetlane jako 0.1, a nie jako 0.100000001490116119384765625, jest to, że tak naprawdę reprezentuje zakres liczb od 13421772.5/134217728 do 13421773.5/134217728 (to jest od 0.0999999977648258209228515625 do 0.1000000052154064178466796875); nie byłoby sensu dodawać dodatkowych cyfr, wskazując, że liczba jest większa niż 0,100, gdy może być mniejsza, ani użyć ciągu dziewięciu, co oznacza, że ​​liczba jest mniejsza niż 0,100, gdy może być większa.

Rzut podwójny na zmienną wybierze wartość zmiennoprzecinkową, której zakres wartości obejmuje zakres podwójny reprezentowany przez podwójny. Należy zauważyć, że chociaż operacja ta jest nieodwracalna, wynik operacji będzie ogólnie poprawny arytmetycznie; Jedynym czasem, w którym nie byłaby w 100% poprawna arytmetycznie, byłby rzut o jeden podwójny, którego zakres był precyzyjnie wyśrodkowany na granicy między dwoma pływakami. W takiej sytuacji system wybierałby pływak po jednej lub drugiej stronie zakresu podwójnego; jeśli podwójna faktycznie reprezentuje liczbę po niewłaściwej stronie zakresu, wynikowa konwersja byłaby nieco niedokładna.

W praktyce, wspomniana powyżej niewielka niedokładność prawie nigdy nie ma znaczenia, ponieważ "zakres wartości" reprezentowany przez typ zmiennoprzecinkowy jest w praktyce nieco większy niż wskazano powyżej. Wykonywanie obliczeń (takich jak dodanie) na dwóch liczbach, które mają pewną dozę niepewności, przyniesie wynik z większą niepewnością, ale system nie będzie śledził, ile istnieje niepewność. Niemniej jednak, o ile nie wykona się kilkudziesięciu operacji na pływaku lub tysięcy operacji na podwójnej, ilość niepewności będzie zazwyczaj na tyle mała, aby się nie martwić.

Należy zauważyć, że rzucanie pływaka do podwójnego jest w rzeczywistości znacznie bardziej niebezpiecznym działaniem niż rzut podwójny na spławik, nawet jeśli Java pozwala na to bezwarunkowo bez ostrzeżenia, ale skrzek na to ostatnie. Przesunięcie pływaka do podwójnego powoduje, że system wybiera podwójny, którego zakres jest wyśrodkowany wokół środka zakresu pływaka. Będzie to prawie zawsze skutkowało wartością, której faktyczna niepewność jest znacznie większa niż byłaby typowa dla liczb podwójnej precyzji. Na przykład, jeśli rzucisz 0.1f, aby podwoić, wynikowe podwójne reprezentuje liczbę w zakresie od 0,10000000149011611 do 0,10000000149011613, mimo że liczba, którą ma reprezentować (jedna dziesiąta), jest relatywnie nigdzie w pobliżu tego zakresu.