Korzystanie z Javy.matematyka.MathContext

Ostatnio próbowałem zrozumieć użycie Javy.matematyka.MathContext ale nie zrozumiał poprawnie. Jest używany do zaokrąglania w java.math.BigDecimal. Jeśli tak, dlaczego nie zaokrągla cyfry dziesiętne, ale nawet część mantissa.

Z API docs dowiedziałem się, że jest zgodny ze standardem określonym w specyfikacjach ANSI X3.274-1996 i ANSI X3.274-1996/AM 1-2000, ale nie udało mi się ich odczytać online.

Daj mi znać, jeśli masz jakiś pomysł.
Author: Ganesh Chowdhary Sadanala, 2008-08-11

5 answers

@jatan

Dzięki za odpowiedź. To ma sens. Czy możesz mi wyjaśnić MathContext w kontekście metody BigDecimal # round.

Nie ma nic specjalnego w BigDecimal.round() vs. każda inna metoda BigDecimal. We wszystkich przypadkach MathContext określa liczbę cyfr znaczących i technikę zaokrąglania. Zasadniczo istnieją dwie części każdego MathContext. Jest precyzja, jest też RoundingMode.

Precyzja ponownie określa liczbę cyfr znaczących. Jeśli więc podasz 123 jako liczbę i poprosisz o 2 znaczące cyfry, otrzymasz 120. To może być jaśniejsze, jeśli myślisz w kategoriach notacji naukowej.

123 będzie 1.23e2 w notacji naukowej. Jeśli zachowasz tylko 2 znaczące cyfry, otrzymasz 1.2e2 lub 120. Zmniejszając liczbę cyfr znaczących, zmniejszamy precyzję, z jaką możemy określić liczbę.

Część RoundingMode określa jak powinien poradzić sobie z utratą precyzji. Aby ponownie użyć przykładu, jeśli użyjesz 123 jako liczby i poprosisz o 2 znaczące cyfry, zmniejszysz swoją precyzję. Z RoundingMode z HALF_UP (tryb domyślny), 123 stanie się 120. Z RoundingMode z CEILING, otrzymasz 130.

Na przykład:

System.out.println(new BigDecimal("123.4",
                   new MathContext(4,RoundingMode.HALF_UP)));
System.out.println(new BigDecimal("123.4",
                   new MathContext(2,RoundingMode.HALF_UP)));
System.out.println(new BigDecimal("123.4",
                   new MathContext(2,RoundingMode.CEILING)));
System.out.println(new BigDecimal("123.4",
                   new MathContext(1,RoundingMode.CEILING)));

Wyjścia:

123.4
1.2E+2
1.3E+2
2E+2

Widać, że zarówno precyzja, jak i tryb zaokrąglania wpływają na wyjście.

 48
Author: Derek Park,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 06:06:48

Aby zaokrąglać tylko część ułamkową bigdecimalu, sprawdź metodę BigDecimal.setScale(int newScale, int roundingMode).

Np. zmiana liczby z trzema cyframi po przecinku na jedną z dwoma cyframi i zaokrąglenie w górę:

BigDecimal original = new BigDecimal("1.235");
BigDecimal scaled = original.setScale(2, BigDecimal.ROUND_HALF_UP);

Wynikiem tego jest BigDecimal o wartości 1.24 (ze względu na regułę zaokrąglania)

 70
Author: Øystein Øvrebø,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-16 12:55:27

Dodałbym tu kilka przykładów. Nie znalazłem ich w poprzednich odpowiedziach, ale uważam je za przydatne dla tych, którzy mogą wprowadzić w błąd znaczące cyfryz liczbą miejsc dziesiętnych. Załóżmy, że mamy taki kontekst:

MathContext MATH_CTX = new MathContext(3, RoundingMode.HALF_UP);

Dla tego kodu:

BigDecimal d1 = new BigDecimal(1234.4, MATH_CTX);
System.out.println(d1);

Jest całkowicie jasne, że Twój wynik jest 1.23E+3, jak chłopaki powiedzieli powyżej. Pierwsze cyfry znaczące to 123...

Ale co w tym przypadku:

BigDecimal d2 = new BigDecimal(0.000000454770054, MATH_CTX);
System.out.println(d2);

Twoja liczba nie będzie zaokrąglona do 3 miejsca po przecinku - dla kogoś może to nie być intuicyjne i warte podkreślenia. Zamiast tego zostanie zaokrąglona do pierwszych 3 znaczących cyfr , które w tym przypadku są "4 5 4". Tak więc powyższy kod daje 4.55E-7 a nie 0.000 Jak ktoś mógł się spodziewać.

Podobne przykłady:

BigDecimal d3 = new BigDecimal(0.001000045477, MATH_CTX);
 System.out.println(d3);  // 0.00100

BigDecimal d4 = new BigDecimal(0.200000477, MATH_CTX);
 System.out.println(d4);   // 0.200

BigDecimal d5 = new BigDecimal(0.000000004, MATH_CTX);
    System.out.println(d5); //4.00E-9

Mam nadzieję, że ten oczywisty, ale istotny przykład będzie pomocny...

 15
Author: guitar_freak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-11 08:05:46

Jeśli dobrze cię rozumiem, wygląda na to, że oczekujesz, że MathContext będzie kontrolował, ile cyfr powinno być zachowanych po przecinku. Nie po to to jest. Określa liczbę cyfr do zachowania, Razem . Więc jeśli określisz, że chcesz 3 znaczące cyfry, to wszystko, co dostaniesz.

Na przykład to:

System.out.println(new BigDecimal("1234567890.123456789",
                   new MathContext(20)));

System.out.println(new BigDecimal("1234567890.123456789",
                   new MathContext(10)));

System.out.println(new BigDecimal("1234567890.123456789",
                   new MathContext(5)));

Wyświetli:

1234567890.123456789
1234567890
1.2346E+9
 4
Author: Derek Park,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 05:44:00

To nie dla Zabawy. W zasadzie znalazłem jakiś przykład online, który mówi o użyciu MathContext do zaokrąglania ilości/liczb przechowywanych w BigDecimal.

Na przykład,

Jeśli MathContext jest skonfigurowane na precision = 2 i rounding mode = ROUND_HALF_EVEN

BigDecimal Number = 0.5294, jest zaokrąglone do 0.53

Więc pomyślałem, że to nowsza technika i użyłem jej do zaokrąglania. Jednak przerodziło się to w koszmar, ponieważ zaczęło zaokrąglać nawet część liczby.

Dla przykład,

Number = 1.5294 jest zaokrąglone do 1.5

Number = 10.5294 jest zaokrąglone do 10

Number = 101.5294 jest zaokrąglone do 100

.... i tak dalej

Więc nie jest to zachowanie oczekiwałem dla zaokrąglania(jako precyzja = 2).

Wydaje się mieć jakąś logikę, ponieważ z pattera mogę powiedzieć, że pobiera pierwsze dwie cyfry (ponieważ precyzja to 2) liczby, a następnie dodaje 0 do no. cyfr stają się takie same jak kwota nieograniczona (sprawdź przykład 101.5294 ...)

 4
Author: jatanp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-23 10:09:10