Использование java.math.MathContext - PullRequest
45 голосов
/ 11 августа 2008

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

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

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

Ответы [ 5 ]

59 голосов
/ 16 ноября 2010

Для округления только дробной части 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 (из-за правила округления)

40 голосов
/ 11 августа 2008

@ 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

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

12 голосов
/ 10 апреля 2014

Я бы добавил сюда несколько примеров. Я не нашел их в предыдущих ответах, но нахожу их полезными для тех, кто может вводить в заблуждение значащие цифры с числом десятичных знаков . Предположим, у нас есть такой контекст:

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

Надеюсь, этот очевидный, но соответствующий пример был бы полезен ...

4 голосов
/ 11 августа 2008

Это не для удовольствия. На самом деле я нашел несколько онлайн-примеров, в которых указано использование 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 ...)

4 голосов
/ 11 августа 2008

Если я вас правильно понимаю, похоже, что вы ожидаете, что 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
...