Использование java.math.MathContext
-
08-06-2019 - |
Вопрос
Недавно я попытался понять использование java.math.MathContext но не смог правильно понять.Используется ли он для округления java.math.BigDecimal
.Если да, то почему не округляются десятичные цифры, а даже часть мантиссы.
Из документации API я узнал, что он соответствует стандарту, указанному в ANSI X3.274-1996
и ANSI X3.274-1996/AM 1-2000
спецификации, но мне не удалось их прочитать в Интернете.
Пожалуйста, дайте мне знать, если у вас есть идеи по этому поводу.
Решение
@jatan
Спасибо за ответ.Это имеет смысл.Не могли бы вы объяснить мне MathContext в контексте метода BigDecimal#round.
В этом нет ничего особенного BigDecimal.round()
против. любой другой BigDecimal
метод.Во всех случаях MathContext
определяет количество значащих цифр и метод округления.По сути, каждый состоит из двух частей. MathContext
.Есть точность, а есть еще RoundingMode
.
Точность снова определяет количество значащих цифр.Итак, если вы укажете 123
как число и попросите две значащие цифры, вы получите 120
.Возможно, будет яснее, если подумать в терминах научной записи.
123
было бы 1.23e2
в научной записи.Если вы сохраните только 2 значащие цифры, то вы получите 1.2e2
, или 120
.Уменьшая количество значащих цифр, мы уменьшаем точность, с которой мы можем указать число.
А RoundingMode
часть определяет, как нам следует справляться с потерей точности.Чтобы повторно использовать пример, если вы используете 123
как число и запросите две значащие цифры, вы снизили точность.С RoundingMode
из HALF_UP
(режим по умолчанию), 123
станет 120
.С RoundingMode
из CEILING
, ты получишь 130
.
Например:
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)));
Выходы:
123.4
1.2E+2
1.3E+2
2E+2
Вы можете видеть, что на результат влияют как точность, так и режим округления.
Другие советы
Чтобы округлить только дробную часть BigDecimal, проверьте BigDecimal.setScale(int newScale, int roundingMode)
метод.
Например.чтобы изменить число с тремя цифрами после запятой на число с двумя цифрами и округлением вверх:
BigDecimal original = new BigDecimal("1.235");
BigDecimal scaled = original.setScale(2, BigDecimal.ROUND_HALF_UP);
Результатом этого является BigDecimal со значением 1,24 (из-за правила округления).
Я бы добавил сюда несколько примеров.Я не нашел их в предыдущих ответах, но считаю их полезными для тех, кто может вводить в заблуждение. значащие цифры с количеством десятичные знаки.Предположим, у нас есть такой контекст:
MathContext MATH_CTX = new MathContext(3, RoundingMode.HALF_UP);
Для этого кода:
BigDecimal d1 = new BigDecimal(1234.4, MATH_CTX);
System.out.println(d1);
совершенно ясно, что ваш результат 1.23E+3
как ребята сказали выше.Первые значащие цифры 123...
Но что в данном случае:
BigDecimal d2 = new BigDecimal(0.000000454770054, MATH_CTX);
System.out.println(d2);
твой номер не будет округляться до 3 знаков после запятой - для кого-то это может быть не интуитивно понятно и стоит подчеркнуть.Вместо этого оно будет округлено до первые 3 значащие цифры, в данном случае это «4 5 4».Таким образом, приведенный выше код приводит к 4.55E-7
и не в 0.000
как кто-то мог ожидать.
Похожие примеры:
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
Надеюсь, этот очевидный, но актуальный пример будет полезен...
Если я вас правильно понимаю, похоже, вы ожидаете, что MathContext будет контролировать, сколько цифр должно оставаться после десятичной точки.Это не для этого.Он указывает, сколько цифр следует сохранить, общий.Поэтому, если вы укажете, что вам нужны 3 значащие цифры, это все, что вы получите.
Например, это:
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)));
выведет:
1234567890.123456789
1234567890
1.2346E+9
Это не для развлечения.На самом деле я нашел в Интернете пример, в котором указано использование MathContext
для округления сумм/числ, хранящихся в BigDecimal.
Например,
Если MathContext
настроен так, чтобы иметь precision = 2
и rounding mode = ROUND_HALF_EVEN
BigDecimal Number = 0.5294
, является округлый к 0.53
Поэтому я подумал, что это более новая техника, и использовал ее для округления.Однако это обернулось кошмаром, потому что начало округлять даже ментиссскую часть числа.
Например,
Number = 1.5294
округляется до 1.5
Number = 10.5294
округляется до 10
Number = 101.5294
округляется до 100
....и так далее
Так что это не то поведение, которое я ожидал от округления (поскольку точность = 2).
Кажется, в этом есть некоторая логика, потому что по шаблону я могу сказать, что он берет первые две цифры (поскольку точность равна 2) числа, а затем добавляет 0 до номера.цифр становится таким же, как неокругленная сумма (проверьте пример 101,5294...)