Как округлить число до n знаков после запятой в Java
У меня есть задача: мне нужен метод для преобразования числа с плавающей запятой (double) в строку, который использует метод округления "вверх при 5" — то есть, если цифра, которую нужно округлить, равна 5, она всегда округляется вверх до следующего целого числа. Это стандартный способ округления, который многие ожидают в большинстве ситуаций.
Кроме того, я хотел бы, чтобы отображались только значимые цифры, то есть не должно быть никаких завершающих нулей.
Я знаю один способ сделать это — использовать метод String.format
:
String.format("%.5g%n", 0.912385);
Это возвращает:
0.91239
Что прекрасно, однако этот метод всегда отображает числа с 5 десятичными знаками, даже если они незначимы:
String.format("%.5g%n", 0.912300);
Это возвращает:
0.91230
Другой способ — использовать DecimalFormat
:
DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);
Это возвращает:
0.91238
Но, как видно, этот способ использует округление "половинное-смешанное", то есть округляет вниз, если предыдущее число четное. То, что я хотел бы получить:
0.912385 -> 0.91239
0.912300 -> 0.9123
Как лучший способ добиться этого в Java?
4 ответ(ов)
Предположим, что value
— это тип double
. Вы можете сделать так:
(double)Math.round(value * 100000d) / 100000d
Это обеспечит точность до 5 знаков после запятой. Количество нулей после единицы указывает на количество десятичных знаков.
Чтобы получить объект BigDecimal
из значения типа double
, можно использовать следующий код:
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);
После этого, чтобы получить строку из этого объекта BigDecimal
, просто вызовите метод toString
, или используйте метод toPlainString
(доступен начиная с Java 5), если вам нужна строка в «обычном» формате без экспоненциального представления.
Вот пример программы:
package trials;
import java.math.BigDecimal;
public class Trials {
public static void main(String[] args) {
int yourScale = 10;
System.out.println(BigDecimal.valueOf(0.42344534534553453453 - 0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
}
}
В этом примере создается объект BigDecimal
, в который помещается результат вычисления, и масштабируем его до 10 знаков после запятой, используя режим округления ROUND_HALF_UP
.
Как уже отмечали некоторые пользователи, правильным ответом будет использование либо DecimalFormat
, либо BigDecimal
. У типов с плавающей запятой нет точных десятичных разрядов, поэтому невозможно округлить или обрезать число до определённого количества разрядов с их помощью. Необходимо работать с десятичной системой счисления, и именно это делают указанные классы.
Я публикую следующий код как контрпример к любым ответам в этой теме и действительно по всему StackOverflow (и другим ресурсам), которые рекомендуют умножение, за которым следует обрезка и деление. Тем, кто пропагандирует эту технику, необходимо объяснить, почему следующий код выдаёт неправильный результат более чем в 92% случаев.
public class RoundingCounterExample
{
static float roundOff(float x, int position)
{
float a = x;
double temp = Math.pow(10.0, position);
a *= temp;
a = Math.round(a);
return (a / (float)temp);
}
public static void main(String[] args)
{
float a = roundOff(0.0009434f,3);
System.out.println("a="+a+" (a % .001)="+(a % 0.001));
int count = 0, errors = 0;
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
int scale = 2;
double factor = Math.pow(10, scale);
d = Math.round(d * factor) / factor;
if ((d % 0.01) != 0.0)
{
System.out.println(d + " " + (d % 0.01));
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
}
Вывод этой программы:
10001 trials 9251 errors
ПРИМЕЧАНИЕ: Чтобы ответить на некоторые комментарии ниже, я переделал часть тестового цикла с модулем, используя BigDecimal
и new MathContext(16)
для операции модуля следующим образом:
public static void main(String[] args)
{
int count = 0, errors = 0;
int scale = 2;
double factor = Math.pow(10, scale);
MathContext mc = new MathContext(16, RoundingMode.DOWN);
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
d = Math.round(d * factor) / factor;
BigDecimal bd = new BigDecimal(d, mc);
bd = bd.remainder(new BigDecimal("0.01"), mc);
if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
{
System.out.println(d + " " + bd);
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
Результат:
10001 trials 4401 errors
Вы можете использовать класс DecimalFormat
для форматирования числа с двумя десятичными знаками. Вот пример кода на Java:
double d = 3.76628729;
DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal = Double.valueOf(newFormat.format(d));
В этом примере мы создаем объект DecimalFormat
, указывая шаблон форматирования "#.##"
, который позволяет отображать число с двумя знаками после десятичной точки. После этого мы форматируем число d
и возвращаем его в виде объекта типа Double
.
Инициализация ArrayList в одну строчку
Что значит "Не удалось найти или загрузить основной класс"?
Как установить Java 8 на Mac
Почему в RecyclerView отсутствует onItemClickListener()?
Что значит 'synchronized'?