Więc mam podwójny zestaw równy 1234, chcę przesunąć miejsce dziesiętne, aby uzyskać 12,34
Aby to zrobić, mnożę 0,1 do 1234 dwa razy, trochę w ten sposób
double x = 1234;
for(int i=1;i<=2;i++)
{
x = x*.1;
}
System.out.println(x);
Spowoduje to wydrukowanie wyniku „12.340000000000002”
Czy istnieje sposób, bez prostego formatowania go do dwóch miejsc po przecinku, aby poprawnie zapisać podwójny zapis 12,34?
x /= 100;
?Odpowiedzi:
Jeśli używasz
double
lubfloat
, powinieneś użyć zaokrąglenia lub spodziewać się błędów zaokrągleń. Jeśli nie możesz tego zrobić, użyjBigDecimal
.Problem polega na tym, że 0.1 nie jest dokładną reprezentacją i wykonując dwa obliczenia, potęgujesz ten błąd.
Jednak 100 można dokładnie przedstawić, więc spróbuj:
który drukuje:
To działa, ponieważ
Double.toString(d)
wykonuje niewielkie zaokrąglenia w Twoim imieniu, ale nie jest to dużo. Jeśli zastanawiasz się, jak mogłoby to wyglądać bez zaokrąglania:wydruki:
Krótko mówiąc, zaokrąglanie jest nieuniknione w przypadku rozsądnych odpowiedzi zmiennoprzecinkowych, niezależnie od tego, czy robisz to jawnie, czy nie.
Uwaga:
x / 100
ix * 0.01
nie są dokładnie takie same, jeśli chodzi o błąd zaokrąglania. Dzieje się tak, ponieważ błąd zaokrąglenia dla pierwszego wyrażenia zależy od wartości x, podczas0.01
gdy drugi ma stały błąd zaokrąglenia.wydruki
źródło
1234/100
, tak jak to zrobiłeś, tak naprawdę nie wpływa na podstawowy problem - powinno być dokładnie równe pisaniu1234 * 0.01
./100
i*0.01
są sobie równe, ale nie OP*0.1*0.1
.Nie - jeśli chcesz dokładnie przechowywać wartości dziesiętne, użyj
BigDecimal
.double
po prostu nie może dokładnie reprezentować liczby takiej jak 0,1, tak samo jak nie można zapisać dokładnie jednej trzeciej przy użyciu skończonej liczby cyfr dziesiętnych.źródło
jeśli to tylko formatowanie, spróbuj printf
wynik
źródło
System.out.printf()
jest to właściwa droga.W oprogramowaniu finansowym często używa się liczb całkowitych zamiast groszy. W szkole uczono nas, jak używać stałego punktu zamiast pływającego, ale zwykle są to potęgi dwóch. Przechowywanie groszy w liczbach całkowitych można również nazwać „punktem stałym”.
Na zajęciach zapytano nas ogólnie, jakie liczby można dokładnie przedstawić w bazie.
Dla
base=p1^n1*p2^n2
... możesz przedstawić dowolne N, gdzie N = n * p1 ^ m1 * p2 ^ m2.Niech
base=14=2^1*7^1
... możesz reprezentować 1/7 1/14 1/28 1/49, ale nie 1/3Wiem o oprogramowaniu finansowym - przekonwertowałem raporty finansowe Ticketmaster z VAX asm na PASCAL. Mieli swój własny formatln () z kodami na grosze. Przyczyną konwersji było to, że 32-bitowe liczby całkowite już nie wystarczały. +/- 2 miliardy groszy to 20 milionów dolarów, a to przelane na Mistrzostwa Świata lub Igrzyska Olimpijskie, zapomniałem.
Przysięgałem zachować tajemnicę. No cóż. W środowisku akademickim, jeśli dobrze, to publikujesz; w przemyśle zachowujesz to w tajemnicy.
źródło
możesz spróbować reprezentacji liczb całkowitych
źródło
r
jest mniejsza niż 10, nie występuje wypełnienie 0 i 1204 dałoby wynik 12,4. Prawidłowy ciąg formatujący jest bardziej podobny do „% d.% 02d”Jest to spowodowane sposobem, w jaki komputery przechowują liczby zmiennoprzecinkowe. Nie robią tego dokładnie. Jako programista powinieneś przeczytać ten przewodnik zmiennoprzecinkowy, aby zapoznać się z próbami i problemami związanymi z obsługą liczb zmiennoprzecinkowych.
źródło
Zabawne, że liczne posty wspominają o używaniu BigDecimal, ale nikt nie zadaje sobie trudu, aby podać poprawną odpowiedź na podstawie BigDecimal? Ponieważ nawet w przypadku BigDecimal nadal możesz popełnić błąd, jak pokazano w tym kodzie
Daje ten wynik
Konstruktor BigDecimal wyraźnie wspomina, że lepiej jest użyć konstruktora String niż konstruktora liczbowego. Na najwyższą precyzję ma również wpływ opcjonalny MathContext.
Zgodnie z BigDecimal Javadoc możliwe jest utworzenie BigDecimal, który jest dokładnie równy 0,1, pod warunkiem, że użyjesz konstruktora String.
źródło
Tak jest. Każda podwójna operacja może spowodować utratę dokładności, ale poziom dokładności jest różny dla każdej operacji i można go zminimalizować, wybierając odpowiednią sekwencję operacji. Na przykład podczas mnożenia zbioru liczb, przed pomnożeniem najlepiej posortować zestaw według wykładnika.
Opisuje to każda przyzwoita książka o chrupaniu liczb. Na przykład: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
I aby odpowiedzieć na twoje pytanie:
Użyj dzielenia zamiast mnożenia, w ten sposób otrzymasz poprawny wynik.
źródło
Nie, ponieważ typy zmiennoprzecinkowe Java (a właściwie wszystkie typy zmiennoprzecinkowe) są kompromisem między rozmiarem a dokładnością. Chociaż są one bardzo przydatne w wielu zadaniach, jeśli potrzebujesz dowolnej precyzji, powinieneś użyć
BigDecimal
.źródło