Недавно я попытался понять использование java.math.MathContext, но не смог понять правильно. Используется ли он для округления в java.math.BigDecimal. Если да, то почему не округляются десятичные цифры, а даже часть мантиссы.
Из документации по API я узнал, что он соответствует стандарту, указанному в спецификациях ANSI X3.274-1996 и ANSI X3.274-1996/AM 1-2000, но я не заставил их читать онлайн.
Пожалуйста, дайте мне знать, если у вас есть какие-либо идеи по этому поводу.




Если я вас правильно понимаю, похоже, вы ожидаете, что 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
Это не совсем вопрос для BigDecimal. При использовании BigDecimal вместо этого следует указывать количество значащих цифр. Сколько цифр после десятичной точки следует определить при форматировании для отображения. Вы можете контролировать это с помощью String.format () или DecimalFormat.format ().
@Drew Я думаю, вы можете использовать для этого метод BigDecimal.setScale. Первый параметр (масштаб) указывает, сколько чисел вы хотите сохранить после десятичной точки, а второй параметр (roundingMode) указывает желаемое поведение округления.
@Drew Здесь - это ответ, который объясняет это более подробно.
Это не для развлечения. На самом деле я нашел какой-то онлайн-пример, в котором говорилось об использовании 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 ...)
так что вы можете предложить вместо этого решение?
@jatan
Thanks for you answer. It makes sense. Can you please explain me MathContext in the context of BigDecimal#round method.
В BigDecimal.round()против. нет ничего особенного ни в каком другом методе BigDecimal. Во всех случаях MathContext указывает количество значащих цифр и метод округления. По сути, каждый MathContext состоит из двух частей. Есть точность, а также RoundingMode.
Точность снова определяет количество значащих цифр. Итак, если вы укажете 123 как число и попросите 2 значащих цифры, вы получите 120. Это может быть яснее, если вы мыслите в терминах научных обозначений.
123 будет 1.23e2 в научном представлении. Если вы сохраните только 2 значащие цифры, вы получите 1.2e2 или 120. Уменьшая количество значащих цифр, мы уменьшаем точность, с которой мы можем указать число.
Часть RoundingMode определяет, как мы должны справиться с потерей точности. Чтобы повторно использовать пример, если вы используете 123 в качестве числа и запрашиваете 2 значащих цифры, вы уменьшаете свою точность. С 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
Вы можете видеть, что и точность, и режим округления влияют на результат.
Есть ли способ отформатировать 123.4 с использованием 2 значащих цифр, чтобы получить строку типа 120? Это две значащие цифры (как и 1.2E2), потому что конечные нули обычно не включаются при извлечении значащих цифр, если нет конечной десятичной дроби. И для моих целей, если число, которое нужно отформатировать, было 103.4, меня не волнует, что вы не можете сказать, что в полученном номере 100 есть 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 (из-за правила округления).
Это тот ответ, которого хотят люди, работая с фиксированными десятичными числами и сохраняя фиксированное количество десятичных знаков. Не знаю, почему Sun напутала здесь с «точностью» в стиле с плавающей запятой, но это то, что такое MathContext.
Это даже работает для округления BigDecimal без дробной части: original.setScale(0, RoundingMode.HALF_UP);
Я бы добавил сюда несколько примеров. Я не нашел их в предыдущих ответах, но считаю их полезными для тех, кто может ввести в заблуждение значащие цифры с номером десятичные знаки. Предположим, у нас есть такой контекст:
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 значащие цифры, которое в данном случае равно «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
Надеюсь, этот очевидный, но соответствующий пример будет вам полезен ...
Спасибо. Как выбрать количество цифр после запятой?