public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
Powyższy kod drukuje:
11.399999999999
Jak mogę to po prostu wydrukować (lub móc go używać jako) 11.4?
java
floating-point
double
precision
Deinumite
źródło
źródło
Odpowiedzi:
Jak wspominali inni, prawdopodobnie będziesz chciał użyć tej
BigDecimal
klasy, jeśli chcesz mieć dokładną reprezentację 11.4.Teraz krótkie wyjaśnienie, dlaczego tak się dzieje:
Te
float
idouble
prymitywne typy w Javie są zmiennoprzecinkowych liczb, których liczba jest przechowywana jako binarnej reprezentacji ułamek i wykładnik.Mówiąc dokładniej, wartość zmiennoprzecinkowa podwójnej precyzji, taka jak
double
typ, jest wartością 64-bitową, gdzie:Te części są łączone w celu uzyskania
double
reprezentacji wartości.(Źródło: Wikipedia: Podwójna precyzja )
Szczegółowy opis sposobu obsługi wartości zmiennoprzecinkowych w języku Java znajduje się w Sekcji 4.2.3: Typy, formaty i wartości zmiennoprzecinkowe specyfikacji języka Java.
Na
byte
,char
,int
,long
typy stałoprzecinkowych numery, które są dokładne representions liczb. W przeciwieństwie do liczb stałoprzecinkowych, liczby zmiennoprzecinkowe czasami (można bezpiecznie założyć „przez większość czasu”) nie będą w stanie zwrócić dokładnej reprezentacji liczby. To jest powód, dla którego kończysz11.399999999999
jako wynik5.6 + 5.8
.Wymagając dokładnej wartości, takiej jak 1.5 lub 150.1005, będziesz chciał użyć jednego z typów stałoprzecinkowych, które będą w stanie dokładnie reprezentować liczbę.
Jak już kilkakrotnie wspominano, Java ma
BigDecimal
klasę, która obsługuje bardzo duże liczby i bardzo małe liczby.Z dokumentacji Java API dla
BigDecimal
klasy:Pojawiło się wiele pytań dotyczących przepełnienia stosu związanych z liczbami zmiennoprzecinkowymi i ich dokładnością. Oto lista powiązanych pytań, które mogą być interesujące:
Jeśli naprawdę chcesz przejść do drobiazgowych szczegółów liczb zmiennoprzecinkowych, zapoznaj się z artykułem Co każdy informatyk powinien wiedzieć o arytmetyce zmiennoprzecinkowej .
źródło
BigDecimal
jest znacznie wolniejsze niżdouble
w tym przypadku, nie jest potrzebne, ponieważ liczba podwójna ma 15 miejsc po przecinku, wystarczy ją zaokrąglić.Na przykład, gdy wprowadzasz podwójną liczbę,
33.33333333333333
otrzymana wartość jest w rzeczywistości najbliższą reprezentowalną wartością podwójnej precyzji, która jest dokładnie:Dzieląc to przez 100, otrzymujemy:
która również nie może być reprezentowana jako liczba podwójnej precyzji, więc ponownie jest zaokrąglana do najbliższej możliwej do przedstawienia wartości, czyli dokładnie:
Kiedy drukujesz tę wartość, jest ona ponownie zaokrąglana do 17 cyfr dziesiętnych, dając:
źródło
Jeśli chcesz przetwarzać wartości jako ułamki, możesz utworzyć klasę Fraction, która zawiera pole licznika i mianownika.
Napisz metody dodawania, odejmowania, mnożenia i dzielenia, a także metodę toDouble. W ten sposób można uniknąć pływaków podczas obliczeń.
EDYCJA: Szybka realizacja,
źródło
numerator
idenominator
powinno byćint
? Dlaczego miałbyś chcieć precyzji zmiennoprzecinkowej?Zauważ, że miałbyś ten sam problem, gdybyś użył arytmetyki dziesiętnej o ograniczonej precyzji i chciałbyś sobie poradzić z 1/3: 0,333333333 * 3 to 0,999999999, a nie 1,00000000.
Niestety, 5,6, 5,8 i 11,4 po prostu nie są liczbami okrągłymi w systemie binarnym, ponieważ obejmują one piąte. Więc ich reprezentacja zmiennoprzecinkowa nie jest dokładna, tak jak 0,3333 nie jest dokładnie 1/3.
Jeśli wszystkie używane liczby nie są liczbami dziesiętnymi, a chcesz uzyskać dokładne wyniki, użyj funkcji BigDecimal. Lub, jak powiedzieli inni, jeśli twoje wartości są jak pieniądze w tym sensie, że wszystkie są wielokrotnością 0,01 lub 0,001, lub coś podobnego, pomnóż wszystko przez stałą potęgę 10 i użyj int lub long (dodawanie i odejmowanie to trywialne: uważaj na mnożenie).
Jeśli jednak jesteś zadowolony z binarnego do obliczeń, ale chcesz po prostu wydrukować rzeczy w nieco bardziej przyjaznym formacie, wypróbuj
java.util.Formatter
lubString.format
. W ciągu formatu określ precyzję mniejszą niż pełna precyzja double. Do 10 cyfr znaczących, powiedzmy, 11,399999999999 to 11,4, więc wynik będzie prawie tak samo dokładny i bardziej czytelny dla człowieka w przypadkach, gdy wynik binarny jest bardzo zbliżony do wartości wymagającej tylko kilku miejsc po przecinku.Precyzja do określenia zależy trochę od tego, ile matematyki wykonałeś na swoich liczbach - ogólnie im więcej zrobisz, tym więcej błędów będzie się kumulować, ale niektóre algorytmy kumulują go znacznie szybciej niż inne (nazywane są „niestabilnymi”, w przeciwieństwie do „stabilnego” w odniesieniu do błędów zaokrąglania). Jeśli wszystko, co robisz, to dodawanie kilku wartości, to domyślam się, że upuszczenie tylko jednego miejsca po przecinku precyzji rozwiąże problem. Eksperyment.
źródło
Jeśli naprawdę potrzebujesz precyzyjnej matematyki, możesz zajrzeć do klasy java.math.BigDecimal w języku Java. Oto dobry artykuł firmy Oracle / Sun na temat przypadku BigDecimal . Chociaż nigdy nie można reprezentować 1/3 jak ktoś wspomniał, to może mieć uprawnienia do decydowania, jak dokładnie chcesz dokładny wynik będzie. setScale () jest Twoim przyjacielem .. :)
Ok, ponieważ w tej chwili mam zbyt dużo czasu, oto przykład kodu, który odnosi się do twojego pytania:
i aby podłączyć mój nowy ulubiony język, Groovy, oto ładniejszy przykład tego samego:
źródło
Całkiem pewny, że mogłeś to przekształcić w przykład z trzema wierszami. :)
Jeśli potrzebujesz dokładnej precyzji, użyj BigDecimal. W przeciwnym razie możesz użyć liczb całkowitych pomnożonych przez 10 ^ z dowolną dokładnością.
źródło
Jak zauważyli inni, nie wszystkie wartości dziesiętne można przedstawić jako dwójkowe, ponieważ dziesiętne są oparte na potęgach 10, a dwójkowe na potęgach dwóch.
Jeśli liczy się precyzja, użyj BigDecimal, ale jeśli potrzebujesz tylko przyjaznego wyniku:
Da tobie:
źródło
Napotykasz ograniczenie precyzji typu double.
Java.Math ma kilka funkcji arytmetycznych o dowolnej precyzji.
źródło
Nie możesz, ponieważ 7.3 nie ma skończonej reprezentacji w systemie binarnym. Najbliższy możliwy do uzyskania to 2054767329987789/2 ** 48 = 7,3 + 1/1407374883553280.
Spójrz na http://docs.python.org/tutorial/floatingpoint.html aby uzyskać dalsze wyjaśnienia. (Jest na stronie Pythona, ale Java i C ++ mają ten sam „problem”).
Rozwiązanie zależy od tego, na czym dokładnie polega Twój problem:
źródło
źródło
Użyj java.math.BigDecimal
Podwójne są wewnętrznie ułamkami binarnymi, więc czasami nie mogą reprezentować ułamków dziesiętnych do dokładnego dziesiętnego.
źródło
Pomnóż wszystko przez 100 i zapisz w długości centów.
źródło
Komputery przechowują liczby w postaci binarnej i nie mogą w rzeczywistości reprezentować dokładnie takich liczb, jak 33,333333333 lub 100,0. To jedna z najtrudniejszych rzeczy w używaniu podwójnych. Będziesz musiał po prostu zaokrąglić odpowiedź, zanim pokażesz ją użytkownikowi. Na szczęście w większości aplikacji i tak nie potrzebujesz tylu miejsc po przecinku.
źródło
Liczby zmiennoprzecinkowe różnią się od liczb rzeczywistych tym, że dla dowolnej liczby zmiennoprzecinkowej istnieje następna wyższa liczba zmiennoprzecinkowa. To samo co liczby całkowite. Nie ma liczby całkowitej od 1 do 2.
Nie ma możliwości przedstawienia 1/3 jako liczby zmiennoprzecinkowej. Pod nim jest pływak, a nad nim pływak, a między nimi jest pewna odległość. A 1/3 jest w tej przestrzeni.
Apfloat for Java twierdzi, że działa z liczbami zmiennoprzecinkowymi o dowolnej precyzji, ale nigdy go nie używałem. Chyba warto zajrzeć. http://www.apfloat.org/apfloat_java/
Podobne pytanie zadano tutaj przed biblioteką zmiennoprzecinkową Java o wysokiej precyzji
źródło
Podwojenia to przybliżenia liczb dziesiętnych w źródle Java. Widzisz konsekwencje niedopasowania podwójnej (która jest wartością zakodowaną binarnie) i źródła (które jest zakodowane dziesiętnie).
Java tworzy najbliższe przybliżenie binarne. Możesz użyć java.text.DecimalFormat, aby wyświetlić lepiej wyglądającą wartość dziesiętną.
źródło
Użyj BigDecimal. Pozwala nawet określić reguły zaokrąglania (takie jak ROUND_HALF_EVEN, które zminimalizują błąd statystyczny poprzez zaokrąglenie do równego sąsiada, jeśli oba są tej samej odległości, tj. Zarówno 1,5, jak i 2,5 zaokrąglenia do 2).
źródło
Krótka odpowiedź: zawsze używaj BigDecimal i upewnij się, że używasz konstruktora z String argumentem , a nie podwójnym.
Wracając do przykładu, poniższy kod wydrukuje 11.4, jak chcesz.
źródło
Sprawdź BigDecimal, rozwiązuje problemy z arytmetyką zmiennoprzecinkową w ten sposób.
Nowe wezwanie wyglądałoby tak:
Użyj setScale (), aby ustawić liczbę dokładności miejsca dziesiętnego, która ma być używana.
źródło
Dlaczego nie użyć metody round () z klasy Math?
źródło
Jeśli nie masz innego wyboru niż używanie podwójnych wartości, możesz użyć poniższego kodu.
źródło
Nie marnuj wysiłku przy użyciu BigDecimal. W 99,99999% przypadków nie jest to potrzebne. java double type jest oczywiście przybliżony, ale w prawie wszystkich przypadkach jest wystarczająco dokładny. Pamiętaj, że masz błąd na czternastej cyfrze znaczącej.To jest naprawdę nieistotne!
Aby uzyskać ładny wynik, użyj:
źródło