Avatar ·

Как округлить число до нужного количества знаков после запятой в Java?

Всем привет! Я новичок в Java, и у меня возникла проблема с округлением чисел. Мне нужно, чтобы при вычислениях результат всегда отображался с двумя знаками после запятой, как в калькуляторе. Например, я делю 10 на 3 и получаю 3.3333333333333335, а хочу видеть 3.33. Или умножаю 2.345 на 100 и получаю 234.5, а нужно 234.50. Я пробовал использовать `Math.round()`, но он округляет до целого числа. Пробовал писать что-то вроде `Math.round(number * 100) / 100`, но это работает только для целых чисел, а если число отрицательное или большое — вылезают артефакты. Ещё я слышал про `DecimalFormat`, но не понимаю, как его правильно настроить. Подскажите, какой самый надёжный способ округлить число до N знаков после запятой в Java? Желательно с примерами, чтобы я мог сразу скопировать код. Пример моего кода, который не работает так, как надо: ```java double price = 123.456789; double rounded = Math.round(price * 100) / 100; System.out.println(rounded); // Выводит 123.0, а нужно 123.46 ``` Заранее спасибо! <<>>

Лучший способ: BigDecimal и setScale

Самый надёжный и точный способ округления чисел с плавающей точкой в Java — использование класса BigDecimal. В отличие от double и float, которые имеют погрешности двоичного представления, BigDecimal работает с десятичной арифметикой и даёт предсказуемый результат.

Решение с BigDecimal

Вот универсальный метод, который округляет число до N знаков после запятой:

import java.math.BigDecimal;
import java.math.RoundingMode;

public class RoundingExample {
    public static double roundToNDecimalPlaces(double value, int places) {
        if (places < 0) throw new IllegalArgumentException("Количество знаков не может быть отрицательным");
        
        BigDecimal bd = BigDecimal.valueOf(value);
        bd = bd.setScale(places, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }

    public static void main(String[] args) {
        double price = 123.456789;
        double rounded = roundToNDecimalPlaces(price, 2);
        System.out.println(rounded); // 123.46

        double division = 10.0 / 3.0;
        System.out.println(roundToNDecimalPlaces(division, 2)); // 3.33

        double negative = -2.345;
        System.out.println(roundToNDecimalPlaces(negative, 2)); // -2.35
    }
}

Почему это работает?

  • BigDecimal.valueOf(value) — преобразует double в BigDecimal без потери точности (важно: не используйте new BigDecimal(double), он может дать неожиданный результат из-за двоичного представления).
  • setScale(places, RoundingMode.HALF_UP) — устанавливает количество знаков после запятой и режим округления. HALF_UP — это стандартное математическое округление (0.5 округляется вверх).
  • bd.doubleValue() — преобразует результат обратно в double, если нужно. Если вы работаете с деньгами или точными значениями, лучше оставить результат как BigDecimal.

Альтернативный вариант: вывод с форматированием

Если вам нужно только отобразить число с определённым количеством знаков, а не хранить его в переменной, используйте String.format():

double price = 123.456789;
String formatted = String.format("%.2f", price);
System.out.println(formatted); // 123.46

Но помните: String.format() возвращает строку, а не число. Для вычислений это не подходит.

Чего делать не стоит

  • Не используйте Math.round(value * 100) / 100 — это даёт целочисленное деление, если не привести к double.
  • Не используйте new BigDecimal(double) — он может сохранить неточное двоичное представление (например, new BigDecimal(2.345) даст 2.3449999999999998).
  • Не используйте float для точных расчётов — у него ещё меньше точности, чем у double.

Итог

Для округления чисел в Java используйте BigDecimal.setScale() с подходящим RoundingMode. Это гарантирует точность и предсказуемость. Если нужно просто показать число — используйте String.format().

<<>>

Альтернативный подход: DecimalFormat для форматирования

Если ваша задача — только вывести число с определённым количеством знаков после запятой (например, для отчёта или UI), можно воспользоваться классом DecimalFormat из пакета java.text. Он удобен для работы с шаблонами и локализацией.

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

public class DecimalFormatExample {
    public static void main(String[] args) {
        double price = 123.456789;
        
        // Шаблон: ровно 2 знака после запятой
        DecimalFormat df = new DecimalFormat("#.##");
        df.setRoundingMode(java.math.RoundingMode.HALF_UP);
        
        String formatted = df.format(price);
        System.out.println(formatted); // 123.46

        // Для фиксированного количества знаков (например, 2.00 вместо 2)
        DecimalFormat dfFixed = new DecimalFormat("0.00");
        dfFixed.setRoundingMode(java.math.RoundingMode.HALF_UP);
        System.out.println(dfFixed.format(2.0)); // 2.00
    }
}

Когда это полезно?

  • Когда нужно учитывать локаль (например, в Германии запятая — разделитель, а точка — для тысяч).
  • Когда нужно гибко управлять отображением (например, показывать минимум 2 знака, но не больше 4).

Недостатки

  • DecimalFormat возвращает строку, а не число.
  • При неправильном шаблоне может отбросить дробную часть (например, #.## для 123.0 покажет "123", а не "123.00").
  • Для точных вычислений всё равно лучше использовать BigDecimal.

Совет: Если вы работаете с деньгами, используйте BigDecimal для хранения и вычислений, а DecimalFormat — только для вывода на экран. Это даёт и точность, и красивый вывод.