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

То, что я хотел бы, - это метод преобразования двойного числа в строку, которая округляется с использованием метода половинного увеличения - то есть, если десятичное число должно быть округлено до 5, оно всегда округляется до следующего числа. Это стандартный метод округления, которого большинство людей ожидают в большинстве ситуаций.

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

Я знаю один способ сделать это, чтобы использовать String.formatметод:

String.format("%.5g%n", 0.912385);

возвращает:

0.91239

это здорово, однако всегда отображает числа с 5 десятичными разрядами, даже если они не имеют значения:

String.format("%.5g%n", 0.912300);

возвращает:

0.91230

Другой метод заключается в использовании DecimalFormatter:

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

возвращает:

0.91238

Однако, как вы можете видеть, для этого используется округление до половины. То есть будет округляться, если предыдущая цифра четная. Что я хотел бы это:

0.912385 -> 0.91239
0.912300 -> 0.9123

Каков наилучший способ достичь этого в Java?

30.09.2008 16:01:34
30 ОТВЕТОВ
РЕШЕНИЕ

Используйте setRoundingMode, установите RoundingModeявно для решения вашей проблемы с половиной четного раунда, а затем используйте шаблон формата для вашего требуемого вывода.

Пример:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

дает вывод:

12
123.1235
0.23
0.1
2341234.2125

РЕДАКТИРОВАТЬ : оригинальный ответ не касается точности двойных значений. Это хорошо, если вас не волнует, округляется ли он вверх или вниз. Но если вы хотите точного округления, вам нужно принять во внимание ожидаемую точность значений. Значения с плавающей точкой имеют двоичное представление внутри. Это означает, что значение, подобное 2.7735, на самом деле не имеет этого точного внутреннего значения. Это может быть немного больше или немного меньше. Если внутреннее значение немного меньше, оно не будет округляться до 2,7740. Чтобы исправить эту ситуацию, вы должны знать о точности значений, с которыми вы работаете, и добавлять или вычитать это значение перед округлением. Например, если вы знаете, что ваши значения имеют точность до 6 цифр, то для округления до половины значения добавьте эту точность к значению:

Double d = n.doubleValue() + 1e-6;

Чтобы округлить, вычтите точность.

743
9.01.2020 14:00:29
Это, вероятно, лучшее решение, представленное на данный момент. Причина, по которой я не заметил эту возможность, когда впервые увидел класс DecimalFormat, заключается в том, что он был представлен только в Java 1.6. К сожалению, я ограничен использованием 1.5, но это будет полезно знать в будущем.
Alex Spurling 1.10.2008 13:07:09
Я попробовал это с:, "#.##"округления HALF_UP. 256.335f-> "256.33"... (пример взят из комментариев к ответу @ asterite).
bigstones 11.12.2015 11:43:18
Пожалуйста, будьте осторожны, так как DecimalFormat зависит от вашей текущей локальной конфигурации, вы не можете получить точку в качестве разделителя. Я лично предпочитаю ответ
Gomino 2.03.2016 17:01:35
Также помните, что вы не должны ожидать, что DecimalFormat будет поточно-ориентированным. Согласно документации Java : Десятичные форматы обычно не синхронизируются. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, он должен быть синхронизирован извне.
CGK 10.08.2016 03:05:50
как мне сделать так, чтобы оно правильно округлялось, чтобы оно не
user4919188 20.10.2016 08:00:25

Предполагая , что valueэто double, вы можете сделать:

(double)Math.round(value * 100000d) / 100000d

Это для точности 5 цифр. Количество нулей указывает количество десятичных знаков.

465
16.09.2015 12:17:03
ОБНОВЛЕНИЕ: Я только что подтвердил, что делать это быстрее, чем используя DecimalFormat. Я зациклился, используя DecimalFormat 200 раз, и этот метод. Для завершения 200 циклов DecimalFormat потребовалось 14 мс, этот метод занял менее 1 мс. Как я и подозревал, это быстрее. Если вам платят за такт, это то, что вы должны делать. Я удивлен, что Крис Кадмор даже сказал бы то, что он сказал, чтобы быть честным. выделение объектов всегда дороже, чем приведение примитивов и использование статических методов (Math.round (), а не decimalFormat.format ()).
Andi Jay 10.07.2012 14:35:56
Эта техника не работает более чем в 90% случаев. -1.
user207421 2.10.2012 03:12:55
Действительно, это не удается Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775.
Robert Tupelo-Schneck 3.10.2012 18:51:09
Будьте очень осторожны при использовании этого метода (или любого округления с плавающей запятой). Это не так просто 265.335. Промежуточный результат 265,335 * 100 (с точностью до 2 цифр) составляет 26533,499999999996. Это означает, что он округляется до 265,33. При преобразовании чисел с плавающей запятой в действительные десятичные числа просто возникают проблемы. Смотрите ответ EJP здесь на stackoverflow.com/a/12684082/144578
Sebastiaan van den Broek 14.11.2013 13:51:49
@SebastiaanvandenBroek: Ух ты, я никогда не знал, что так легко получить неправильный ответ. Однако, если кто-то работает с неточными числами, нужно признать, что любое значение не является точным . 265.335действительно означает 265.335 += tolerance, что допуск зависит от предыдущих операций и диапазона входных значений. Мы не знаем истинного, точного значения. При краевых значениях любой ответ, возможно, является правильным. Если нам нужно быть точным, мы не должны работать в два раза. failЗдесь не в преобразовании обратно в два раза. Это в OP, думая, что он может полагаться на входящее, 265.335как являющееся именно этим.
ToolmakerSteve 28.08.2015 03:17:50
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

получит тебя BigDecimal. Для того, чтобы получить строку из него, просто назвать это 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));
    }
190
1.03.2017 08:00:31
Это мое предпочтительное решение. Еще короче: BigDecimal.valueOf (doubleVar) .setScale (yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (double val) на самом деле вызывает Double.toString () под капотом;)
Etienne Neveu 9.02.2010 10:59:03
Приятно. Не срезайте углы и используйте его, new BigDecimal(doubleVar)поскольку вы можете столкнуться с проблемами с округлением чисел с плавающей запятой
Edd 26.01.2015 17:57:29
@ Как ни странно, проблема округления возникает в том случае, когда СебастианванденБрук упоминает в комментарии к ответу астериты. double val = 265.335;, BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.34, но (new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.33.
ToolmakerSteve 28.08.2015 03:41:46
@ToolmakerSteve. Это потому, что использование нового BigDecimal с двойным напрямую принимает значение double и пытается использовать его для создания BigDecimal, тогда как при использовании BigDecimal.valueOf или формы tostring сначала анализирует его в строку (более точное представление) перед преобразованием ,
MetroidFan2002 30.08.2015 05:09:41

Вы также можете использовать

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

чтобы убедиться, что у вас есть конечные 0.

115
30.09.2008 16:58:12
Я считаю , что одна из целей этого вопроса в том , что «должно не быть никаких замыкающие нули».
Lunchbox 20.11.2012 20:10:16
Для этого вопроса опера не хотел нулей, но это именно то, что я хотел. Если у вас есть список чисел с 3 десятичными разрядами, вы хотите, чтобы все они имели одинаковые цифры, даже если это 0.
Tom Kincaid 8.04.2014 17:06:39
Вы забыли уточнитьRoundingMode.
IgorGanapolsky 29.09.2015 21:27:08
@IgorGanapolsky по умолчанию Decimal modeиспользуетRoundingMode.HALF_EVEN.
EndermanAPM 27.09.2016 15:40:01

Как отметили некоторые другие, правильный ответ должен использовать либо 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
86
18.12.2017 03:53:47
Хитрость в том, что во всех ваших 9251 ошибках напечатанный результат все еще корректен.
Didier L 9.08.2013 07:52:02
@DidierL Меня это не удивляет. Мне очень повезло, что я сделал «Численные методы» в качестве моего самого первого компьютерного курса и познакомился в самом начале с тем, что с плавающей точкой можно и нельзя делать. Большинство программистов довольно расплывчаты в этом.
user207421 13.08.2013 09:55:19
Все, что вы делаете, это опровергаете, что плавающее число точно не представляет много десятичных значений, что, я надеюсь, мы все понимаем. Не то, что округление вызывает проблемы. Как вы признаете, цифры по-прежнему печатаются, как и ожидалось.
Peter Lawrey 2.03.2014 11:21:53
Ваш тест не пройден, возьмите round () и тест не пройдёт 94% времени. ideone.com/1y62CY печатает. 100 trials 94 errorsВы должны начать с теста, который проходит успешно , и показать, что введение округления нарушает тест.
Peter Lawrey 2.03.2014 11:26:56
Опровержение опровергнуто здесь. Использование Math.round для этого диапазона doubleбез ошибок ideone.com/BVCHh3
Peter Lawrey 2.03.2014 11:42:36

Предположим, у вас есть

double d = 9232.129394d;

вы можете использовать BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

или без BigDecimal

d = Math.round(d*100)/100.0d;

с обоими решениями d == 9232.13

83
14.09.2015 22:30:33
Я думаю, что это лучшее решение для пользователей Java 1.5 (и ниже). Один комментарий, не используйте режим округления HALF_EVEN, так как он имеет разностное поведение для нечетных и четных чисел (например, 2,5 округления до 2, а 5,5 округления до 6), если только это не то, что вы хотите.
IcedDante 16.07.2012 19:11:42
Первое решение верное: второе не работает. Смотрите здесь для доказательства.
user207421 19.03.2013 23:27:24
@EJP: Даже первое решение с RoundingMode.HALF_UPнеправильным. Попробуйте это с 1.505. Правильный способ заключается в использовании BigDecimal.valueOf(d).
Matthias Braun 29.03.2014 00:58:35
Матиас Браун, решение в порядке, следовательно, 31 ups. 1.505 десятичного числа хранится в двойных числах с плавающей запятой как 1.50499998, если вы хотите взять 1.505 и преобразовать из двойного в десятичное, то сначала вы должны преобразовать его в Double.toString (x) затем поместите его в BigDecimal (), но это очень медленно, и в первую очередь побеждает цель использовать double для скорости.
hamish 13.07.2014 08:22:41
Запустил цикл 100k с BigDecimal (заняло 225 мс) и Math.round (2 мс), и вот время ... Взятое время: 225 миллисекунд для преобразования, используя для: 9232.13 Время взятое: 2 миллисекунды для преобразования : 9232.13 techiesinfo.com
user1114134 31.07.2014 19:32:28

Вы можете использовать класс DecimalFormat.

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));
59
29.09.2011 07:01:59
Любая причина, почему Double.valueOf()был выбран Double.parseDouble()? valueOf()Метод возвращает Doubleобъект, в то время как parseDouble()будет возвращать doubleпримитивно. При способе написания текущего кода вы также применяете автоматическую распаковку к возврату, чтобы привести его к примитиву, ожидаемому вашей twoDoubleпеременной, дополнительной операции с байт-кодом. Я бы изменил ответ, чтобы использовать parseDouble()вместо этого.
ecbrodie 3.12.2015 06:25:05
Double.parseDouble()нужен Stringвклад.
Ryde 5.05.2016 23:40:46

Как к Java Real в постах это решение, которое также совместим для версий до Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
38
1.09.2009 11:14:32
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
33
30.09.2008 16:09:26
да, это именно то, что math.round делает для положительных чисел, но вы пробовали это с отрицательными числами? люди используют math.round в других решениях, чтобы также покрыть случай отрицательных чисел.
hamish 13.07.2014 08:39:36
Примечание: Math.floor(x + 0.5)иMath.round(x)
Peter Lawrey 2.02.2018 11:25:30

@Milhous: превосходный десятичный формат для округления:

Вы также можете использовать

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

чтобы убедиться, что у вас есть конечные 0.

Я хотел бы добавить, что этот метод очень хорош в обеспечении фактического числового механизма округления - не только визуально, но и при обработке.

Гипотетический: вы должны внедрить механизм округления в программу с графическим интерфейсом. Чтобы изменить точность / точность результатов, просто измените формат каретки (то есть в скобках). Так что:

DecimalFormat df = new DecimalFormat("#0.######");
df.format(0.912385);

вернется в качестве вывода: 0.912385

DecimalFormat df = new DecimalFormat("#0.#####");
df.format(0.912385);

вернется в качестве вывода: 0.91239

DecimalFormat df = new DecimalFormat("#0.####");
df.format(0.912385);

вернется в качестве вывода: 0.9124

[РЕДАКТИРОВАТЬ: также, если формат каретки такой («# 0. ############»), и вы вводите десятичное число, например, 3.1415926, для аргумента, DecimalFormat не производит никакого мусора ( например, конечные нули) и вернет: 3.1415926.. если вы склонны к этому. Конечно, это немного многословно для симпатий некоторых разработчиков - но, эй, у него мало места в памяти во время обработки и его очень легко реализовать.]

Таким образом, красота DecimalFormat заключается в том, что он одновременно обрабатывает внешний вид строки, а также уровень точности округления. Ergo: вы получаете два преимущества по цене одной реализации кода. ;)

30
3.07.2011 19:55:34
Если вам действительно нужны десятичные числа для расчета (и не только для вывода), не используйте двоичный формат с плавающей запятой, например double. Используйте BigDecimal или любой другой десятичный формат.
Paŭlo Ebermann 3.07.2011 19:57:23

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

  1. DecimalFormat # setRoundingMode () :

    DecimalFormat df = new DecimalFormat("#.#####");
    df.setRoundingMode(RoundingMode.HALF_UP);
    String str1 = df.format(0.912385)); // 0.91239
  2. BigDecimal # setScale ()

    String str2 = new BigDecimal(0.912385)
        .setScale(5, BigDecimal.ROUND_HALF_UP)
        .toString();

Вот предложение, какие библиотеки вы можете использовать, если хотите doubleв результате. Я бы не рекомендовал это для преобразования строк, так как double может быть не в состоянии точно представить, что вы хотите (см., Например, здесь ):

  1. Точность от Apache Commons Math

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
  2. Функции от Colt

    double rounded = Functions.round(0.00001).apply(0.912385)
  3. Утилиты из Века

    double rounded = Utils.roundDouble(0.912385, 5)
20
23.05.2017 12:02:47

Вы можете использовать следующий метод утилиты:

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}
18
27.04.2011 15:56:25
@mariolpantunes: это не удастся. Попробуйте это: round(1.005,2);илиround(0.50594724957626620092, 20);
Matthias Braun 29.03.2014 01:02:43
Оно работает. Но неинформативно с плавающей точкой и удваивается приблизительные значения. Давайте рассмотрим ваш первый пример. Если вы напечатаете вывод заинтересованныхInZeroDPs перед Math.round, он напечатает 100.49999999999999. Вы потеряли точность как
mariolpantunes 31.03.2014 09:04:49
двойной это быстро! десятичное медленно. компьютеры не утруждают себя обработкой своего мышления в десятичной записи. Вы должны отказаться от некоторой десятичной точности, чтобы скорость с плавающей запятой удваивалась.
hamish 13.07.2014 09:16:02
@ Hamish Вопрос в точности, а не в скорости.
user207421 25.04.2016 10:00:46

Краткое решение:

   public static double round(double value, int precision) {
      int scale = (int) Math.pow(10, precision);
      return (double) Math.round(value * scale) / scale;
  }

См. Также https://stackoverflow.com/a/22186845/212950. Спасибо jpdymond за это.

13
26.07.2018 14:41:08

Вы можете использовать BigDecimal

BigDecimal value = new BigDecimal("2.3");
value = value.setScale(0, RoundingMode.UP);
BigDecimal value1 = new BigDecimal("-2.3");
value1 = value1.setScale(0, RoundingMode.UP);
System.out.println(value + "n" + value1);

См .: http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/

9
22.07.2014 09:49:35

Попробуйте это: org.apache.commons.math3.util.Precision.round (double x, int scale)

См. Http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html.

Домашняя страница библиотеки математики Apache Commons: http://commons.apache.org/proper/commons-math/index.html

Внутренняя реализация этого метода:

public static double round(double x, int scale) {
    return round(x, scale, BigDecimal.ROUND_HALF_UP);
}

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new BigDecimal
               (Double.toString(x))
               .setScale(scale, roundingMethod))
               .doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}
7
20.02.2014 05:04:07

Поскольку я не нашел полного ответа на эту тему, я собрал класс, который должен обрабатывать это правильно, с поддержкой:

  • Форматирование : легко форматировать двойные строки с определенным количеством знаков после запятой
  • Синтаксический анализ : разобрать форматированное значение обратно, чтобы удвоить
  • Язык : форматировать и анализировать, используя язык по умолчанию
  • Экспоненциальная запись : начните использовать экспоненциальную запись после определенного порога

Использование довольно просто :

(Ради этого примера я использую собственную локаль)

public static final int DECIMAL_PLACES = 2;

NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);

String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"

double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345

Вот класс :

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;

public class NumberFormatter {

    private static final String SYMBOL_INFINITE           = "\u221e";
    private static final char   SYMBOL_MINUS              = '-';
    private static final char   SYMBOL_ZERO               = '0';
    private static final int    DECIMAL_LEADING_GROUPS    = 10;
    private static final int    EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
    private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation

    private DecimalFormat decimalFormat;
    private DecimalFormat decimalFormatLong;
    private DecimalFormat exponentialFormat;

    private char groupSeparator;

    public NumberFormatter(int decimalPlaces) {
        configureDecimalPlaces(decimalPlaces);
    }

    public void configureDecimalPlaces(int decimalPlaces) {
        if (decimalPlaces <= 0) {
            throw new IllegalArgumentException("Invalid decimal places");
        }

        DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
        separators.setMinusSign(SYMBOL_MINUS);
        separators.setZeroDigit(SYMBOL_ZERO);

        groupSeparator = separators.getGroupingSeparator();

        StringBuilder decimal = new StringBuilder();
        StringBuilder exponential = new StringBuilder("0.");

        for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
            decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
        }

        for (int i = 0; i < decimalPlaces; i++) {
            decimal.append("#");
            exponential.append("0");
        }

        exponential.append("E0");

        decimalFormat = new DecimalFormat(decimal.toString(), separators);
        decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
        exponentialFormat = new DecimalFormat(exponential.toString(), separators);

        decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
        decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
        exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
    }

    public String format(double value) {
        String result;
        if (Double.isNaN(value)) {
            result = "";
        } else if (Double.isInfinite(value)) {
            result = String.valueOf(SYMBOL_INFINITE);
        } else {
            double absValue = Math.abs(value);
            if (absValue >= 1) {
                if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
                    value = Math.floor(value);
                    result = exponentialFormat.format(value);
                } else {
                    result = decimalFormat.format(value);
                }
            } else if (absValue < 1 && absValue > 0) {
                if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
                    result = decimalFormat.format(value);
                    if (result.equalsIgnoreCase("0")) {
                        result = decimalFormatLong.format(value);
                    }
                } else {
                    result = exponentialFormat.format(value);
                }
            } else {
                result = "0";
            }
        }
        return result;
    }

    public String formatWithoutGroupSeparators(double value) {
        return removeGroupSeparators(format(value));
    }

    public double parse(String value, double defValue) {
        try {
            return decimalFormat.parse(value).doubleValue();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return defValue;
    }

    private String removeGroupSeparators(String number) {
        return number.replace(String.valueOf(groupSeparator), "");
    }

}
7
11.11.2016 22:39:16

Если вам действительно нужны десятичные числа для расчета (и не только для вывода), не используйте двоичный формат с плавающей запятой, такой как double.

Use BigDecimal or any other decimal-based format.

Я использую BigDecimal для расчетов, но имейте в виду, что это зависит от размера чисел, с которыми вы имеете дело. В большинстве моих реализаций я считаю, что синтаксический анализ от двойного или целого до длинного вполне достаточен для очень большого числа вычислений.

На самом деле, я недавно использовал parsed-to-Long, чтобы получить точные представления (в отличие от шестнадцатеричных результатов) в графическом интерфейсе для чисел, таких как ################## ############### символы (как пример).

7
30.05.2018 18:33:04

Для достижения этого мы можем использовать этот форматер:

 DecimalFormat df = new DecimalFormat("#.00");
 String resultado = df.format(valor)

или:

DecimalFormat df = new DecimalFormat("0.00"); :

Используйте этот метод, чтобы получить всегда два десятичных знака:

   private static String getTwoDecimals(double value){
      DecimalFormat df = new DecimalFormat("0.00"); 
      return df.format(value);
    }

Определение этого значения:

91.32
5.22
11.5
1.2
2.6

Используя метод, мы можем получить следующие результаты:

91.32
5.22
11.50
1.20
2.60

Демо онлайн.

6
14.03.2018 17:40:39

На всякий случай, если кому-то все еще нужна помощь с этим. Это решение отлично работает для меня.

private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals,  BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();

}

возвращает String с желаемым выходом.

5
3.03.2016 11:43:00
Пожалуйста, укажите причину понижения в комментарии, иначе это то, что мы называем запугиванием.
Asher. M. O 30.07.2019 18:08:52

Я согласен с выбранным ответом использовать DecimalFormat--- или альтернативно BigDecimal.

Пожалуйста, сначала прочтите Обновление ниже!

Однако , если вы действительно хотите , чтобы закруглить двойное значение и получить doubleрезультат значения, вы можете использовать , org.apache.commons.math3.util.Precision.round(..)как упоминалось выше. Реализация использует BigDecimal, медленно и создает мусор.

Аналогичный, но быстрый и не содержащий мусора метод предоставляется DoubleRounderутилитой из библиотеки decimal4j:

 double a = DoubleRounder.round(2.0/3.0, 3);
 double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
 double c = DoubleRounder.round(1000.0d, 17);
 double d = DoubleRounder.round(90080070060.1d, 9);
 System.out.println(a);
 System.out.println(b);
 System.out.println(c);
 System.out.println(d);

Будет выходной

 0.667
 0.666
 1000.0
 9.00800700601E10

См. Https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility.

Отказ от ответственности: я участвую в проекте decimal4j.

Обновление: как отметил @iaforek, DoubleRounder иногда возвращает нелогичные результаты. Причина в том, что он выполняет математически правильное округление. Например, DoubleRounder.round(256.025d, 2)будет округлено до 256,02, потому что двойное значение, представленное как 256,025d, несколько меньше рационального значения 256,025 и, следовательно, будет округлено в меньшую сторону.

Ноты:

  • Это поведение очень похоже на поведение BigDecimal(double)конструктора (но не в valueOf(double)котором используется строковый конструктор).
  • Проблема может быть обойдена с двойным шагом округления сначала до более высокой точности, но это сложно, и я не буду вдаваться в детали

По этим причинам и всему, что упомянуто выше в этом посте, я не могу рекомендовать использовать DoubleRounder .

4
11.06.2017 23:42:35
Есть ли у вас показатели, показывающие, насколько эффективно ваше решение по сравнению с другими?
iaforek 19.05.2017 14:26:24
Я не сравнивал его с другими решениями, но в исходном коде доступен тест jmh: github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/… Я запустил тест на виртуальной машине результаты доступны в виде файла csv здесь: github.com/tools4j/decimal4j/wiki/Performance
marco 19.05.2017 22:23:09
Сбой DoubleRounder в следующих случаях: DoubleRounder.round (256.025d, 2) - ожидаемый: 256.03, фактический: 256.02 или DoubleRounder.round (260.775d, 2) - ожидаемый: 260.78, фактический: 260.77.
iaforek 1.06.2017 22:46:17
@iaforek: это правильно, потому что DoubleRounder выполняет математически правильное округление. Однако я признаю, что это несколько нелогично и, следовательно, обновит мой ответ соответствующим образом.
marco 11.06.2017 23:45:04

Фрагмент кода ниже показывает, как отображать n цифр. Хитрость заключается в том, чтобы установить переменную pp в 1, за которой следуют n нулей. В приведенном ниже примере значение переменной pp имеет 5 нулей, поэтому будут отображаться 5 цифр.

double pp = 10000;

double myVal = 22.268699999999967;
String needVal = "22.2687";

double i = (5.0/pp);

String format = "%10.4f";
String getVal = String.format(format,(Math.round((myVal +i)*pp)/pp)-i).trim();
3
12.11.2012 13:09:52

Если вы используете DecimalFormatдля преобразования doubleв String, это очень просто:

DecimalFormat formatter = new DecimalFormat("0.0##");
formatter.setRoundingMode(RoundingMode.HALF_UP);

double num = 1.234567;
return formatter.format(num);

Есть несколько RoundingModeзначений enum на выбор, в зависимости от требуемого поведения.

3
28.06.2015 11:54:36

Я пришел сюда просто, чтобы получить простой ответ о том, как округлить число. Это дополнительный ответ, чтобы обеспечить это.

Как округлить число в Java

Наиболее распространенным случаем является использование Math.round().

Math.round(3.7) // 4

Числа округляются до ближайшего целого числа. .5Значение округляется. Если вам нужно другое поведение округления, чем это, вы можете использовать одну из других математических функций. Смотрите сравнение ниже.

круглый

Как указано выше, это округляет до ближайшего целого числа. .5десятичные числа округлять вверх. Этот метод возвращает int.

Math.round(3.0); // 3
Math.round(3.1); // 3
Math.round(3.5); // 4
Math.round(3.9); // 4

Math.round(-3.0); // -3
Math.round(-3.1); // -3
Math.round(-3.5); // -3 *** careful here ***
Math.round(-3.9); // -4

перекрывать

Любое десятичное значение округляется до следующего целого числа. Это идет к потолку . Этот метод возвращает double.

Math.ceil(3.0); // 3.0
Math.ceil(3.1); // 4.0
Math.ceil(3.5); // 4.0
Math.ceil(3.9); // 4.0

Math.ceil(-3.0); // -3.0
Math.ceil(-3.1); // -3.0
Math.ceil(-3.5); // -3.0
Math.ceil(-3.9); // -3.0

пол

Любое десятичное значение округляется до следующего целого числа. Этот метод возвращает double.

Math.floor(3.0); // 3.0
Math.floor(3.1); // 3.0
Math.floor(3.5); // 3.0
Math.floor(3.9); // 3.0

Math.floor(-3.0); // -3.0
Math.floor(-3.1); // -4.0
Math.floor(-3.5); // -4.0
Math.floor(-3.9); // -4.0

ечать

Это похоже на округление в том, что десятичные значения округляют до ближайшего целого числа. Однако, в отличие от round, .5значения округляются до четного целого числа. Этот метод возвращает double.

Math.rint(3.0); // 3.0
Math.rint(3.1); // 3.0
Math.rint(3.5); // 4.0 ***
Math.rint(3.9); // 4.0
Math.rint(4.5); // 4.0 ***
Math.rint(5.5); // 6.0 ***

Math.rint(-3.0); // -3.0
Math.rint(-3.1); // -3.0
Math.rint(-3.5); // -4.0 ***
Math.rint(-3.9); // -4.0
Math.rint(-4.5); // -4.0 ***
Math.rint(-5.5); // -6.0 ***
3
20.02.2017 14:42:47
Вы решаете только конкретный случай округления до 0 десятичных знаков. Оригинальный вопрос более общий.
lukas84 5.03.2019 10:18:45

Если вы используете технологию, которая имеет минимальный JDK. Вот способ без каких-либо библиотек Java:

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;
3
7.01.2018 07:10:46
Это не удастся в случаях, когда myVal не меньше 1 и с нулями после десятичной запятой вне значения шкалы. Скажем, у вас есть myVal = 9.00000000912385; Выше вернется 9.0. Я думаю, что мы должны предоставить решение, которое работает во всех случаях myVal. Не специально для стоимости, которую вы указали.
tavalendo 21.06.2019 20:57:31
@ user102859 В вашем примере 9.0 - правильный результат. Я не понимаю, как это не получится.
Craigo 26.06.2019 16:45:21

DecimalFormat - лучший способ вывода, но я не предпочитаю его. Я всегда делаю это все время, потому что он возвращает двойное значение. Так что я могу использовать это больше, чем просто вывод.

Math.round(selfEvaluate*100000d.0)/100000d.0;

ИЛИ

Math.round(selfEvaluate*100000d.0)*0.00000d1;

Если вам нужно большое значение десятичного разряда, вы можете использовать вместо него BigDecimal. В любом случае .0это важно. Без этого округление 0.33333d5 позволяет вернуть 0.33333 и допускается только 9 цифр. Вторая функция без .0проблем с 0.30000 возвращает 0.30000000000000004.

2
25.11.2016 09:06:39

вот мой ответ:

double num = 4.898979485566356;
DecimalFormat df = new DecimalFormat("#.##");      
time = Double.valueOf(df.format(num));

System.out.println(num); // 4.89
2
21.08.2019 03:29:57

Поэтому, прочитав большинство ответов, я понял, что большинство из них не будут точными, на самом деле использование BigDecimalкажется лучшим выбором, но если вы не понимаете, как это RoundingModeработает, вы неизбежно потеряете точность. Я понял это при работе с большими числами в проекте и подумал, что это может помочь другим людям, испытывающим трудности с округлением чисел. Например.

BigDecimal bd = new BigDecimal("1363.2749");
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(bd.doubleValue());

Вы ожидаете получить 1363.28в качестве вывода, но в итоге вы получите 1363.27, чего не ожидаете, если не знаете, что RoundingModeделает. Итак, изучая документы Oracle , вы найдете следующее описание для RoundingMode.HALF_UP.

Режим округления для округления в сторону «ближайшего соседа», если оба соседа не равноудалены, в этом случае округление вверх.

Итак, зная это, мы поняли, что не получим точное округление, если не хотим округлить до ближайшего соседа . Таким образом, чтобы выполнить адекватный раунд, нам нужно было бы выполнить цикл от n-1десятичной дроби до желаемых десятичных знаков. Например.

private double round(double value, int places) throws IllegalArgumentException {

    if (places < 0) throw new IllegalArgumentException();

    // Cast the number to a String and then separate the decimals.
    String stringValue = Double.toString(value);
    String decimals = stringValue.split("\\.")[1];

    // Round all the way to the desired number.
    BigDecimal bd = new BigDecimal(stringValue);
    for (int i = decimals.length()-1; i >= places; i--) {
        bd = bd.setScale(i, RoundingMode.HALF_UP);
    }

    return bd.doubleValue();
}

Это в конечном итоге даст нам ожидаемый результат, который будет 1363.28.

1
18.12.2019 18:59:40

Где dp = десятичное место, которое вы хотите, а значение является двойным.

    double p = Math.pow(10d, dp);

    double result = Math.round(value * p)/p;
0
21.04.2014 23:51:17
Производит 1.0для value = 1.005а dp = 2. Используйте это вместо этого.
Matthias Braun 19.05.2014 14:34:43
это нормально, Мэтт, твой пример недействителен. потому что 1.005 не может быть представлен в двойных числах с плавающей точкой в ​​любом случае. он должен храниться на самом деле выше или ниже 1.005, т. е. он хранится как двойной при компиляции: 1.0049998 (он не сохраняется как десятичный в вашем скомпилированном коде, как вы полагаете, читатели) цель верна, он хранит значения в виде плавающей запятой двойной, где крайние случаи, подобные твоему, в любом случае незначительны. если бы это было так, то вы бы использовали 3dp, затем конвертировали бы его в десятичную, а затем выполняли бы функцию десятичного округления, как и ссылку, которую вы опубликовали.
hamish 13.07.2014 08:15:32
@ Хэмиш Я не понимаю, где Матиас "заставил бы читателей поверить" любую такую ​​вещь, как то, что значение составлено как десятичное число. Не вкладывайте слова в уста других людей.
user207421 28.06.2015 13:02:06

Имейте в виду, что String.format () и DecimalFormat создают строку, используя локаль по умолчанию. Таким образом, они могут написать отформатированное число с точкой или запятой в качестве разделителя между целыми и десятичными частями. Чтобы убедиться, что округленная строка имеет нужный формат, используйте java.text.NumberFormat следующим образом:

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

Будет напечатано на английском языке (независимо от того, какой у вас язык): 0,99 123,57 123,00

Пример взят из Farenda - как правильно конвертировать double в String .

0
10.11.2016 22:24:26

В общем, округление выполняется путем масштабирования: round(num / p) * p

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */
double RoundCorrect(double num, int precision) {
    double c = 0.5 * EPSILON * num;
//  double p = Math.pow(10, precision); //slow
    double p = 1; while (precision--> 0) p *= 10;
    if (num < 0)
        p *= -1;
    return Math.round((num + c) * p) / p;
}

// testing edge cases
RoundCorrect(1.005, 2);   // 1.01 correct
RoundCorrect(2.175, 2);   // 2.18 correct
RoundCorrect(5.015, 2);   // 5.02 correct

RoundCorrect(-1.005, 2);  // -1.01 correct
RoundCorrect(-2.175, 2);  // -2.18 correct
RoundCorrect(-5.015, 2);  // -5.02 correct
0
2.07.2018 16:45:24