Подтвердить что ты не робот

Круглый двойной в Java

Я нашел это замечательное решение для округления:

static Double round(Double d, int precise) {
    BigDecimal bigDecimal = new BigDecimal(d);
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    return bigDecimal.doubleValue();
}

Однако результаты путают:

System.out.println(round(2.655d,2)); // -> 2.65
System.out.println(round(1.655d,2)); // -> 1.66

Почему он дает этот результат? Я использую jre 1.7.0_45.

4b9b3361

Ответ 1

Вы должны заменить

BigDecimal bigDecimal = new BigDecimal(d);

с

BigDecimal bigDecimal = BigDecimal.valueOf(d);

и вы получите ожидаемые результаты:

2.66
1.66

Объяснение из Java API:

BigDecimal.valueOf(double val) - использует двойное каноническое строковое представление, предоставляемое методом Double.toString(). Это предпочтительный способ преобразования double (или float) в BigDecimal.

new BigDecimal (double val) - использует точное десятичное представление двойного двоичного значения с плавающей запятой, и поэтому результаты этого конструктора могут быть несколько непредсказуемыми.

Ответ 2

Вы можете попробовать изменить свою программу следующим образом: -

static Double round(Double d, int precise) 
{
BigDecimal bigDecimal = BigDecimal.valueOf(d);
bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}

Образец Идеона

Success  time: 0.07 memory: 381184 signal:0
Rounded: 2.66
Rounded: 1.66

Success  time: 0.07 memory: 381248 signal:0
Rounded: 2.66
Rounded: 1.66

Причина, по которой вы получаете ожидаемый результат с BigDecimal.valueOf, а не с new BigDecimal, словами Joachim Sauer:

BigDecimal.valueOf(double) будет использовать каноническое строковое представление двойного значения, переданного для создания объекта BigDecimal. Другими словами: значение объекта BigDecimal будет тем, что вы видите, когда выполняете System.out.println(d).

Если вы используете new BigDecimal(d), то BigDecimal будет пытаться представить двойное значение как можно точнее. Обычно это приводит к тому, что гораздо больше цифр сохраняется, чем вы хотите.

Отсюда вытекает некоторая путаница, которую вы смотрите в своей программе.

Из документа Java Doc:

BigDecimal.valueOf(double val). Переводит двойной в BigDecimal, используя двойную каноническую строку представление предоставляемый методом Double.toString(double).

новый BigDecimal (double val) -

Переводит двойной в BigDecimal, который является точным десятичным представление двойного двоичного значения с плавающей запятой. Масштаб возвращаемого BigDecimal является наименьшим значением, таким как (10scale × val) является целым числом. Примечания:

  • Результаты этого конструктора могут быть несколько непредсказуемыми. Можно предположить, что запись нового BigDecimal (0.1) в Java создает BigDecimal, который в точности равен 0,1 (немасштабированное значение 1,
    с шкалой 1), но на самом деле она равна 0,1000000000000000055511151231257827021181583404541015625. Это связано с тем, что 0,1 не может быть представлен точно как двойной (или, для этого материи, как двоичной фракции любой конечной длины). Таким образом, значение который передается конструктору, не точно равен 0,1, несмотря на это.
  • Конструктор String, с другой стороны, вполне предсказуем: запись нового BigDecimal ( "0.1" ) создает BigDecimal который в точности равен 0,1, как и следовало ожидать. Следовательно, это обычно рекомендуется, чтобы конструктор String использовался в предпочтение этому.
  • Когда в качестве источника для BigDecimal следует использовать double, обратите внимание, что этот конструктор обеспечивает точное преобразование; это не дает тот же результат, что и преобразование double в строку с использованием Double.toString(двойной), а затем используя BigDecimal (String)
    конструктор. Чтобы получить этот результат, используйте статическое значение Of (double)
    Метод.

Ответ 3

Этот тестовый пример становится довольно понятным:

public static void main (String[] args) throws java.lang.Exception
{
    System.out.println("Rounded: " + round(2.655d,2)); // -> 2.65
    System.out.println("Rounded: " + round(1.655d,2)); // -> 1.66
}

public static Double round(Double d, int precise)
{       
    BigDecimal bigDecimal = new BigDecimal(d);
    System.out.println("Before round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    System.out.println("After round: " + bigDecimal.toPlainString());
    return bigDecimal.doubleValue();
}

Вывод:

Before round: 2.654999999999999804600747665972448885440826416015625
After round: 2.65
Rounded: 2.65

Before round: 1.6550000000000000266453525910037569701671600341796875
After round: 1.66
Rounded: 1.66

Грязный взломать его будет круглым в два этапа:

static Double round(Double d, int precise)
{
    BigDecimal bigDecimal = new BigDecimal(d);
    System.out.println("Before round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(15, RoundingMode.HALF_UP);
    System.out.println("Hack round: " + bigDecimal.toPlainString());
    bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
    System.out.println("After round: " + bigDecimal.toPlainString());
    return bigDecimal.doubleValue();
}

Здесь 15 находится под максимальным количеством цифр, которое double может представлять в базе 10. Вывод:

Before round: 2.654999999999999804600747665972448885440826416015625
Hack round: 2.655000000000000
After round: 2.66
Rounded: 2.66

Before round: 1.6550000000000000266453525910037569701671600341796875
Hack round: 1.655000000000000
After round: 1.66
Rounded: 1.66

Ответ 4

Как сказано в API

  • Результаты этого конструктора могут быть несколько непредсказуемыми. Можно предположить, что запись нового BigDecimal (0.1) в Java создает BigDecimal, который в точности равен 0,1 (немасштабированное значение 1, с масштаб 1), но на самом деле он равен 0,1000000000000000055511151231257827021181583404541015625. Это связано с тем, что 0,1 не может быть представлен точно как двойной (или, для этого материи, как двоичной фракции любой конечной длины). Таким образом, значение который передается конструктору, не точно равен 0,1, несмотря на это.

  • Конструктор String, с другой стороны, вполне предсказуем: запись нового BigDecimal ( "0.1" ) создает BigDecimal, который точно равна 0,1, как и следовало ожидать. Следовательно, это обычно рекомендуется использовать конструктор String в предпочтение этому.

  • Если в качестве источника BigDecimal следует использовать double, обратите внимание, что этот конструктор обеспечивает точное преобразование; он не дает тот же результат, что и преобразование double в строку, используя Double.toString(double), а затем с помощью BigDecimal (String) конструктор. Чтобы получить этот результат, используйте статическое значение Of (double) Метод.

Из-за этого невозможно точно представлять двойное значение. Поэтому вы должны использовать BigDecimal bigDecimal = BigDecimal.valueOf(d); вместо BigDecimal bigDecimal = new BigDecimal(d);

Ответ 5

Округление a double resp double само по себе не имеет большого смысла, так как тип double datatype не может округлить (легко или вообще?).

Что вы делаете:

  • Возьмите Double d в качестве входного сигнала и int precise количество цифр за разделителем.
  • Создайте BigDecimal из этого d.
  • Правильно округлите BigDecimal.
  • Верните значение double этого BigDecimal, которое больше не имеет округления к нему.

Вы можете пойти двумя способами:

  • Вы можете вернуть BigDecimal, который представляет округленный двойной, и позже решить, что вы с ним делаете.
  • Вы можете вернуть String, представляющий округленный BigDecimal.

Любой из этих способов будет иметь смысл.

Ответ 6

Десятичные числа не могут быть представлены точно в double.

Итак, 2,655 заканчивается тем, что: +2,65499999999999980460074766597

тогда как 1,655 заканчивается тем, что: +1,655000000000000026645352591