Использование java.math.MathContext

Недавно я попытался понять использование java.math.MathContext, но не смог правильно понять. Используется для округления java.math.BigDecimal . Если да, то почему не округляются десятичные цифры, а даже часть мантиссы.

Из API Docs, я узнал , что он соответствует стандарту , указанному в ANSI X3.274-1996 и ANSI X3.274-1996/AM 1-2000 спецификации , но я не получил их читать онлайн.

Пожалуйста, дайте мне знать, если у вас есть какие-либо идеи по этому поводу.

Ответов (5)

Решение

@jatan

Спасибо за ответ. Это имеет смысл. Не могли бы вы объяснить мне MathContext в контексте метода BigDecimal # round.

Нет ничего особенного в 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

Вы можете видеть, что и точность, и режим округления влияют на результат.

Чтобы округлить только дробную часть 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 ...)