Как округлить число до 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?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1 332
0
1 751 964
36
Перейти к ответу Данный вопрос помечен как решенный

Ответы 36

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

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

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

Math.round использует внутреннее приведение к long, которое затем возвращается к двойному.

Chris Cudmore 30.09.2008 20:22

Я думаю, что это лучше, чем использование DecimalFormat. DecimalFormat будет неявно выделять различные объекты, а также String при каждом вызове decimalFormat.Format (). Я считаю, что лучше привести несколько примитивов, чем выделять объекты.

Andi Jay 10.07.2012 00:01

ОБНОВЛЕНИЕ: я только что подтвердил, что делать это НАМНОГО быстрее, чем использовать DecimalFormat. Я использовал DecimalFormat 200 раз, и этот метод. DecimalFormat занял 14 мсек для завершения 200 циклов, этот метод занял менее 1 мс. Как я и подозревал, это быстрее. Если вам платят по тактовому циклу, это то, что вам следует делать. Я удивлен, что Крис Кадмор даже сказал то, что он сказал, если честно. размещение объектов всегда дороже, чем приведение примитивов и использование статических методов (Math.round () в отличие от decimalFormat.format ()).

Andi Jay 10.07.2012 18:35

@Andi Jay Я не говорю, что вы ошибаетесь или правы, но вы не можете сказать, что изолированный тест на одной машине подтверждает вашу теорию.

b1nary.atr0phy 25.08.2012 16:33

Этот метод не работает более чем в 90% случаев. -1.

user207421 02.10.2012 07:12

Действительно, это не удается: Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775.

Robert Tupelo-Schneck 03.10.2012 22:51

Я думаю, умножение будет быстрее деления, поэтому (double)Math.round(value * 100000) * 0.000001

Alvin Wong 15.01.2013 19:19

Я использовал этот метод долгое время, но теперь понял, что это не выход

Victor P. 20.03.2013 02:54

@ RobertTupelo-Schneck Это не работает, потому что double имеет точность только примерно 14 знаков после запятой (что-либо большее, чем это, в любом случае будет неточным)

sambe 19.08.2013 18:18

Будьте очень осторожны при использовании этого метода (или любого округления чисел с плавающей запятой). Он терпит неудачу для чего-то столь же простого, как 265.335. Промежуточный результат 265,335 * 100 (точность 2 цифры) равен 26533,499999999996. Это означает, что он округляется до 265,33. При преобразовании чисел с плавающей запятой в вещественные десятичные числа просто возникают проблемы. См. Ответ EJP здесь, на stackoverflow.com/a/12684082/144578

Sebastiaan van den Broek 14.11.2013 17:51

У @ChrisCudmore есть лучший ответ здесь прокрутите вниз, также Math.pow (10,20) слишком велик, если вам нужно 20 десятичных знаков точности, то double не для вас.

satur9nine 10.04.2014 21:21

Это не работает и с очень маленькими значениями: (double)Math.round(1.005 * 100) / 100 == 1.0 Не используйте его!

Matthias Braun 19.05.2014 18:23

1.005 == 1.00 и 265.335 == 265.33 -> ну и что! это нормально. есть преобразования с плавающей запятой, переходящие в раунд, есть преобразования с плавающей запятой, переходящие в деление, есть преобразования с плавающей запятой, переходящие в 100.0, вам просто нужно принять, что с плавающей запятой является FLOATING POINT ... это приближение! десятичных знаков. double с плавающей запятой быстро !!! конечно, вы теряете некоторую точность, но кого это волнует .. это супер быстро!

hamish 13.07.2014 11:56

конечно, вы теряете некоторую яркую точность ROUND_EVEN / ROUND_UP в этих мизерных случаях, но кого это волнует ... это супер быстро! вот почему программисты в первую очередь используют double. вы все равно округляете эти дополнения до конца, поэтому они, очевидно, не имеют значения для вас или ваших пользователей. в противном случае вы бы вообще не использовали двойное число (то есть с плавающей запятой).

hamish 13.07.2014 12:04

@AndiJay: «Если вам платят по тактовому циклу, это то, что вы должны делать» - я не согласен. Если бы мне платили за такт, я бы использовал DecimalFormat, чтобы сделать мой код как можно медленнее: чем больше тактовых циклов он требует, тем больше для меня денег;)

Franz D. 15.07.2015 10:38

для скорости я преобразовал в int до того, как 5 долларов превратились в 500. Сверхбыстрая математика, всегда округление до 2 десятичных знаков, а для отображения требуется только деление на 100.

darren 05.08.2015 20:54

@SebastiaanvandenBroek: Ух ты, я никогда не знал, что получить неправильный ответ так легко. Однако, если кто-то работает с неточными числами, нужно признать, что любое значение не совсем. 265.335 на самом деле означает 265.335 += tolerance, где допуск зависит от предыдущих операций и диапазона входных значений. Мы не знаем истинной, точной стоимости. При граничных значениях ответ либо, вероятно, правильный. Если быть точным, работать вдвоем не стоит. fail здесь не в двойном преобразовании. В OP думают, что он может полагаться на входящий 265.335 как на это.

ToolmakerSteve 28.08.2015 06:17

Не думаю, что отливка (double) необходима.

shmosel 08.09.2016 09:16

@ToolmakerSteve Если исходное число состоит из трех десятичных знаков, а вам нужно только два, это дает вам правильный (округленный HALF_EVEN) ответ: x = Math.rint(1000*x); x = Math.rint(x/10.0); return x / 100.0;. Очевидно, что результат не совсем 365.34, но это самое близкое приближение, и он распечатывается следующим образом.

maaartinus 21.06.2017 07:47

Этот метод никогда не будет работать надежно. Причина в том, что результатом является не строка, а значение типа double, внутренне имеющее двоичное представление. Но результат, скорее всего, не будет представлен в виде двоичного числа.

fishinear 25.10.2019 18:55

double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;

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

hamish 13.07.2014 12:39

Примечание: Math.floor(x + 0.5) и Math.round(x)

Peter Lawrey 02.02.2018 14:25
Ответ принят как подходящий

Используйте 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

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

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

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

Это, наверное, лучшее решение из представленных на данный момент. Причина, по которой я не заметил эту возможность, когда впервые посмотрел на класс DecimalFormat, заключается в том, что она была представлена ​​только в Java 1.6. К сожалению, я ограничен использованием 1.5, но об этом будет полезно знать в будущем.

Alex Spurling 01.10.2008 17:07

Не работает с десятичным форматом экспоненты, например с форматом ("0.0E00"). В этом примере 0,0155 округляется до 0,0E00 вместо 1,6E-02.

Martin Clemens Bloch 16.10.2014 03:50

Я пробовал это с: "#.##", округляя HALF_UP. 256.335f -> "256.33" ... (пример взят из комментариев к ответу @ asterite).

bigstones 11.12.2015 14:43

Будьте осторожны, так как DecimalFormat зависит от вашей текущей локальной конфигурации, вы не можете получить точку в качестве разделителя. Я лично предпочитаю ответ Астерита ниже

Gomino 02.03.2016 20:01

Также имейте в виду, что не следует ожидать, что DecimalFormat будет потокобезопасным. Согласно Документы Java: Десятичные форматы обычно не синхронизируются. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, он должен быть синхронизирован извне.

CGK 10.08.2016 06:05

как мне сделать так, чтобы он правильно округлял, чтобы он не округлял 0,0004 до 0,001

user4919188 20.10.2016 11:00

Невозможно заставить это работать с HALF_UP: пример DecimalFormat df = new DecimalFormat("0.00"); df.setMaximumFractionDigits(2); df.setRoundingMode(RoundingMode.HALF_UP); df.format(3.775) возвращает 3,77

Thorsten Niehues 11.12.2017 17:20

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

Dawood ibn Kareem 20.02.2018 05:04

@ThorstenNiehues Тот факт, что вы указали его как 3.775, не означает, что он имеет это значение внутри. Внутренне он имеет двоичное значение, и 3,775 не может быть представлен в точности как двоичное значение. Таким образом, он может иметь внутреннее значение, соответствующее 3,77499999999. При округлении всегда добавляйте ожидаемую точность значения.

fishinear 25.10.2019 18:51

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

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

чтобы убедиться, что у вас есть завершающие 0.

Я считаю, что одна из целей вопроса заключалась в том, чтобы «в нет должны быть какие-либо конечные нули».

Lunchbox 21.11.2012 00:10

В этом вопросе оператору не нужны нули, но это именно то, что я хотел. Если у вас есть список чисел с 3 десятичными знаками, вы хотите, чтобы все они имели одинаковые цифры, даже если это 0.

Tom Kincaid 08.04.2014 21:06

Вы забыли указать RoundingMode.

IgorGanapolsky 30.09.2015 00:27

@IgorGanapolsky по умолчанию Decimal mode использует RoundingMode.HALF_EVEN.

EndermanAPM 27.09.2016 18:40

new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

даст вам BigDecimal. Чтобы получить строку, просто вызовите метод BigDecimaltoString или метод 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.valueOf (doubleVar) .setScale (yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (double val) фактически вызывает Double.toString () под капотом;)

Etienne Neveu 09.02.2010 13:59

Хороший. Не срезайте углы и используйте new BigDecimal(doubleVar), так как вы можете столкнуться с проблемами округления чисел с плавающей запятой.

Edd 26.01.2015 20:57

@Edd, интересно, проблема округления возникает в случае, если Себастьян ВанденБрук упоминает в комментарии к ответу астерита. 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 06:41

@ToolmakerSteve Это потому, что использование new BigDecimal с двойным принимает значение double напрямую и пытается использовать его для создания BigDecimal, тогда как при использовании BigDecimal.valueOf или формы tostring сначала разбирает его в строку (более точное представление) перед преобразованием .

MetroidFan2002 30.08.2015 08:09

Практическое руководство по Java от Real сообщения - это решение, которое также совместимо с версиями до Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();

ОБНОВЛЕНИЕ: BigDecimal.ROUND_HALF_UP устарел - используйте RoundingMode

BigDecimal bd = new BigDecimal(Double.toString(number));
bd = bd.setScale(decimalPlaces, RoundingMode.HALF_UP);
return bd.doubleValue();

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

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

Думаю, это лучшее решение для пользователей Java 1.5 (и ниже). Один комментарий, однако, не используйте режим округления HALF_EVEN, поскольку он имеет различное поведение для нечетных и четных чисел (например, 2,5 округления до 2, а 5,5 округления до 6), если это не то, что вы хотите.

IcedDante 16.07.2012 23:11

Первое решение правильное: второе не работает. См. здесь для доказательства.

user207421 20.03.2013 03:27

@EJP: Даже первое решение с RoundingMode.HALF_UP неверно. Попробуйте с 1.505. Правильный способ - использовать BigDecimal.valueOf(d).

Matthias Braun 29.03.2014 04:58

Маттиас Браун, решение в порядке, следовательно, 31 иб. 1,505 десятичного числа хранится в двойном формате с плавающей запятой как 1,50499998, если вы хотите взять 1,505 и преобразовать из двойного в десятичное, тогда вам нужно сначала преобразовать его в Double.toString (x) затем поместите его в BigDecimal (), но это очень медленно и в первую очередь лишает смысла использование double для скорости.

hamish 13.07.2014 12:22

Выполните цикл 100k с BigDecimal (заняло 225 мс) и Math.round (2 мс), и вот время ... Затраченное время: 225 миллисекунд для преобразования с использованием в: 9232,13 Затраченное время: 2 миллисекунды для преобразования в : 9232.13 techiesinfo.com

user1114134 31.07.2014 23:32

это решение в 112 раз медленнее, чем другие методы. Я думаю, если вы не заботитесь о написании быстрого кода, зачем вообще использовать двойной. Если вам нужны правильные числа с основанием 10, НИКОГДА не используйте двойное число.

hamish 04.09.2015 03:00

@IcedDante Режим HALF_EVEN известен как «Банковское округление», часто используемый в финансовых вопросах (отсюда и название). Этот подход более справедлив с математической точки зрения, чем «Школьное округление», которому обычно учат детей. Так как 5 находится точно посередине, всегда округляется в большую сторону («Школа») смещает результаты в сторону больших чисел. Округление Банкира разделяет разницу, поэтому половина значений округляется в большую сторону, а половина в меньшую (при случайном распределении значений).

Basil Bourque 15.09.2015 01:27

Первое решение также неверно в частном случае: BigDecimal bd = new BigDecimal (12.475d) .setScale (2, RoundingMode.HALF_EVEN); bd.doubleValue (); -> Результат: 12.47 Правильный способ: BigDecimal bd = new BigDecimal (Double.toString (12.475d)). SetScale (2, RoundingMode.HALF_EVEN); Причина: новый BigDecimal (12.475d) создает BigDecimal со значением 12.4749999999999996447286321199499070644378662109375

stefan.m 18.02.2016 16:18

Вам нужен BigDecimal.valueOf(d) вместо new BigDecimal(d), если вы хотите избежать ошибок из-за неточности с плавающей запятой.

Dawood ibn Kareem 20.02.2018 06:13

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

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}

@mariolpantunes: Это не удастся. Попробуйте это: round(1.005,2); или round(0.50594724957626620092, 20);

Matthias Braun 29.03.2014 05:02

Оно работает. Но неинформативно float и double - это приближения. Давайте рассмотрим ваш первый пример. Если вы распечатаете вывод заинтересованныхInZeroDPs перед Math.round, он напечатает 100.49999999999999. Вы потеряли точность как таковую Math.round округлите его до 100. Из-за природы или чисел с плавающей запятой и двойных значений есть пограничные случаи, когда он не работает должным образом (подробнее здесь en.wikipedia.org/wiki/Floating_point#Accuracy_problems)

mariolpantunes 31.03.2014 13:04

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

hamish 13.07.2014 13:16

@hamish Вопрос в точности, а не в скорости.

user207421 25.04.2016 13:00

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

You can also use the

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

to make sure you have the trailing 0's.

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

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

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: ты получите два преимущества по цене одной реализации кода. ;)

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

Paŭlo Ebermann 03.07.2011 23:57

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

Use BigDecimal or any other decimal-based format.

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

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

В приведенном ниже фрагменте кода показано, как отображать 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();

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

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

Есть ли причина, по которой Double.valueOf() был выбран вместо Double.parseDouble()? Метод valueOf() возвращает объект Double, а parseDouble() возвращает примитив double. С учетом того, как написан текущий код, вы также применяете автоматическую распаковку к возврату, чтобы преобразовать его в примитив, который ожидает ваша переменная twoDouble, дополнительная операция байт-кода. Я бы изменил ответ на использование parseDouble().

ecbrodie 03.12.2015 09:25

Double.parseDouble() требует ввода String.

Ryde 06.05.2016 02:40

Как отмечали некоторые другие, правильный ответ - использовать 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

Хитрость в том, что во всех ваших 9251 ошибках результат печати все еще верен.

Didier L 09.08.2013 11:52

@DidierL Это доказывает, что «напечатанный результат» и «результат» - не одно и то же. На правильность «результата печати» влияет то, что происходит во время печати. Само по себе умножение и деление не решает проблемы.

user207421 11.08.2013 05:03

@DidierL Меня это не удивляет. Мне посчастливилось пройти «Численные методы» в качестве своего первого курса по вычислениям, и я с самого начала познакомился с тем, что можно и чего нельзя делать с плавающей запятой. Большинство программистов довольно расплывчато об этом.

user207421 13.08.2013 13:55

такой же результат был произведен версией этой программы для Python.

ultrajohn 02.09.2013 09:30

хм, похоже в вашей программе ошибка. System.out.println ("a =" + a + "(a% 0,0001) =" + (a% 0,001));

ultrajohn 02.09.2013 09:46

Что ж, признаюсь, я довольно расплывчато относился к этим «численным методам». Если у вас есть время, я хотел бы узнать ваше мнение о мой предыдущий вопрос.

Matthieu 19.10.2013 10:04

Хотя исходная точка остается в силе (усечение числа с плавающей запятой - это не то же самое, что округление десятичных чисел), этот контрпример вызывает у меня некоторое подозрение. Обратите внимание, что мы знаем, что двойная 0,25 точно равна 1/4, но ((0.25 % 0.01) == 0.0) оценивается как ложь.

Alex 25.10.2013 21:27

Я бы изменил тест с if ((d % 0.01) != 0.0) на if (BigDecimal.valueOf(d).compareTo(new BigDecimal(d)) != 0), что указывает на то, что частота ошибок даже выше, чем первоначально предполагалось (и увеличивается с увеличением шкалы до 99,9% для шкалы 5).

Alex 25.10.2013 21:44

Есть объяснение: (d% 0,01) добавляет неточности к результату и затмевает точность операции округления. Таким образом, это доказывает только то, что в 92% случаев операция модуля не является точной. У меня есть встречный пример: code double d1, d2; d1 = 10.0100000000000000000123D; d2 = 10.0100000D; System.out.println(Math.round(d1 * 100)/100D == d2); // true double d3 = 0.0000090000000000000000000789D; System.out.println(Math.round(d3 * 100)/100D == 0.0D); // true Кстати, я не защищаю эту технику. Склонен к возможному переполнению.

Yuri 27.11.2013 16:06

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

Peter Lawrey 02.03.2014 15:21

Ваш тест не работает, возьмите round (), и тест не проходит в 94% случаев. ideone.com/1y62CY печатает 100 trials 94 errors. Вы должны начать с теста, который прошел успешно, и показать, что введение округления ломает тест.

Peter Lawrey 02.03.2014 15:26

Опровержение, опровергнутое здесь. Использование Math.round для этого диапазона double как отсутствие ошибок ideone.com/BVCHh3

Peter Lawrey 02.03.2014 15:42

@PeterLawrey Хороший замечание, но это правда, только если невозможно округлить 0,001 до точного числа десятичных знаков, которое составляет quod erat manifestrandum.

user207421 04.04.2014 00:43

да, точность означала бы использование памяти в 100 раз больше, а точность означала бы в 100 раз медленнее. делайте собственные выводы относительно того, почему программисты и процессоры любят числа с плавающей запятой в два раза :) через 50 лет, когда процессоры будут в 10 000 раз быстрее .. никто больше не будет заботиться о дублировании :) даже сейчас мы видим более быстрые четырехъядерные процессоры ARM Android .. Вот почему люди, уже работающие в этой ветке, хотят, чтобы точность была важнее скорости.

hamish 04.09.2015 03:07

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

user207421 27.01.2016 06:00

@hamish Для этого требуется десятичная система счисления, а не в 100 раз больше памяти.

user207421 27.01.2016 06:01

Хотя я согласен с вами, поскольку 0.01 не может быть представлен в точности, этот тест плохой. Лучшим тестом было бы преобразовать результат roundOff в BigDecimal и сравнить его с точным результатом, вычисленным с помощью BigDecimal.

geza 05.10.2017 14:18

@geza Я переделал часть цикла, используя BigDecimal/MathContext с точностью до 16 знаков для модуля упругости, и получил 50% отказов.

user207421 18.12.2017 06:48

a *= temp; имеет неточность в округлении значений задачи около границы. Например. x.xx5 и position == 2. Продукт, сохраненный в a, округляется, что время от времени приводит к неверному значению roundOff(). Здесь было бы разумнее сохранить дополнительную точность double с a = Math.round(a * temp);, а не a *= temp; a = Math.round(a);.

chux - Reinstate Monica 27.02.2018 15:56

Попробуйте это: org.apache.commons.math3.util.Precision.round (двойной x, масштаб int)

См .: 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;
        }
    }
}

Где дп = десятичный разряд, который вы хотите, а ценить - это двойник.

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

    double result = Math.round(value * p)/p;

Производит 1.0 для value = 1.005 и dp = 2. Вместо этого используйте это.

Matthias Braun 19.05.2014 18:34

это нормально, Мэтт, твой пример неверен. потому что 1.005 в любом случае не может быть представлен в формате double с плавающей запятой. он должен храниться действительно выше или ниже 1,005, то есть он сохраняется как двойной при компиляции: 1,0049998 (он не сохраняется как десятичный в вашем скомпилированном коде, как вы могли бы подумать читатели) цель правильная, он сохраняет значения как с плавающей запятой двойной, где крайности, подобные вашему, в любом случае несущественны. если бы это было так, то вы использовали бы 3dp, затем преобразовали бы его в десятичное, а затем выполняли бы функцию округления десятичного числа, как и опубликованная вами ссылка.

hamish 13.07.2014 12:15

@hamish Я не понимаю, где Матиас «заставил бы читателей поверить» в такую ​​вещь, как то, что значение компилируется как десятичное. Не вкладывайте слова в уста других людей.

user207421 28.06.2015 16:02

Вы можете использовать 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/

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

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

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

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

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

  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. Утилиты от Weka

    double rounded = Utils.roundDouble(0.912385, 5)
    

Я согласен с выбранным ответом на использование 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.

Есть ли у вас показатели, показывающие, насколько ваше решение эффективно по сравнению с другими?

iaforek 19.05.2017 17:26

Я не сравнивал его с другими решениями, но в исходном коде есть тест jmh: ​​github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/… Я запускал тест на виртуальной машине, результаты доступны в виде файла csv здесь: github.com/tools4j/decimal4j/wiki/Performance

marco 20.05.2017 01:23

DoubleRounder не работает в следующих случаях: DoubleRounder.round (256.025d, 2) - ожидалось: 256,03, фактическое: 256,02 или для DoubleRounder.round (260.775d, 2) - ожидалось: 260,78, фактическое: 260,77.

iaforek 02.06.2017 01:46

@iaforek: это правильно, потому что DoubleRounder выполняет математически правильное округление. Однако я признаю, что это несколько нелогично, и поэтому обновлю свой ответ соответствующим образом.

marco 12.06.2017 02:45

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

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

}

возвращает String с желаемым результатом.

Пожалуйста, укажите в комментарии причину, по которой вы проголосовали против, иначе это то, что мы называем запугиванием.

ashr 30.07.2019 21:08

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

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

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

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

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), "");
    }

}

Имейте в виду, что 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

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

Если считать 5 или n десятичным числом. Может быть, этот ответ решит вашу проблему.

    double a = 123.00449;
    double roundOff1 = Math.round(a*10000)/10000.00;
    double roundOff2 = Math.round(roundOff1*1000)/1000.00;
    double roundOff = Math.round(roundOff2*100)/100.00;

    System.out.println("result:"+roundOff);

Результат будет: 123.01
это можно решить с помощью цикла и рекурсивной функции.

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 return 0.30000000000000004.

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

Как округлить число на 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

потолок

Любое десятичное значение округляется до следующего целого числа. Он переходит в потолокing. Этот метод возвращает 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 ***

вы решаете только частный случай округления до 0 десятичных знаков. Исходный вопрос более общий.

lukas84 05.03.2019 13:18

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

   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 за это.

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

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;

Это не сработает в тех случаях, когда myVal не меньше 1 и с нулями после десятичной дроби, превышающей значение шкалы. Допустим, у вас myVal = 9.00000000912385; Вышеупомянутое вернет 9.0. Я думаю, мы должны предоставить решение, которое будет работать во всех случаях myVal. Не специально для указанного вами значения.

tavalendo 21.06.2019 23:57

@ user102859 В вашем примере 9.0 - правильный результат. Я не понимаю, как это могло потерпеть неудачу.

Craigo 26.06.2019 19:45

Вот лучшая функция, которая правильно округляет крайние случаи, такие как 1.005.

Просто мы добавляем наименьшее возможное значение с плавающей запятой (= 1 ulp; единица в последнем месте) к числу перед округлением. Это переходит к следующему представимому значению после числа, отличному от нуля.

Это небольшая программа для проверки: ideone.com

/**
 * Round half away from zero ('commercial' rounding)
 * Uses correction to offset floating-point inaccuracies.
 * Works symmetrically for positive and negative numbers.
 */
public static double round(double num, int digits) {

    // epsilon correction
    double n = Double.longBitsToDouble(Double.doubleToLongBits(num) + 1);
    double p = Math.pow(10, digits);
    return Math.round(n * p) / p;
}

// test rounding of half
System.out.println(round(0.5, 0));   // 1
System.out.println(round(-0.5, 0));  // -1

// testing edge cases
System.out.println(round(1.005, 2));   // 1.01
System.out.println(round(2.175, 2));   // 2.18
System.out.println(round(5.015, 2));   // 5.02

System.out.println(round(-1.005, 2));  // -1.01
System.out.println(round(-2.175, 2));  // -2.18
System.out.println(round(-5.015, 2));  // -5.02

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

 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

демо онлайн.

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

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

System.out.println(num); // 4.89

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

Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round 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.

Я использовал ниже, как в java 8. Он работает для меня.

    double amount = 1000.431;        
    NumberFormat formatter = new DecimalFormat("##.00");
    String output = formatter.format(amount);
    System.out.println("output = " + output);

Выход:

output = 1000.43

При необходимости можно использовать следующий метод double

double getRandom(int decimalPoints) {
    double a = Math.random();
    int multiplier = (int) Math.pow(10, decimalPoints);
    int b = (int) (a * multiplier);
    return b / (double) multiplier;
}

например getRandom(2)

  1. Чтобы иметь завершающие нули до 5-й позиции
DecimalFormat decimalFormatter = new DecimalFormat("#.00000");
decimalFormatter.format(0.350500); // result 0.350500
  1. Чтобы не тянуть нули до 5-й позиции
DecimalFormat decimalFormatter= new DecimalFormat("#.#####");
decimalFormatter.format(0.350500); // result o.3505

Простой способ сравнения, если количество десятичных знаков ограничено. Вместо DecimalFormat, Math или BigDecimal мы можем использовать Casting!

Вот образец,

public static boolean threeDecimalPlaces(double value1, double value2){
    boolean isEqual = false;
    // value1 = 3.1756 
    // value2 = 3.17
    //(int) (value1 * 1000) = 3175
    //(int) (value2 * 1000) = 3170

    if ((int) (value1 * 1000) == (int) (value2 * 1000)){
        areEqual = true;
    }

    return isEqual;
}

public static double formatDecimal(double amount) {
    BigDecimal amt = new BigDecimal(amount);
    amt = amt.divide(new BigDecimal(1), 2, BigDecimal.ROUND_HALF_EVEN);
    return amt.doubleValue();
}

Тест с использованием Junit

@RunWith(Parameterized.class)
public class DecimalValueParameterizedTest {

  @Parameterized.Parameter
  public double amount;

  @Parameterized.Parameter(1)
  public double expectedValue;

@Parameterized.Parameters
public static List<Object[]> dataSets() {
    return Arrays.asList(new Object[][]{
            {1000.0, 1000.0},
            {1000, 1000.0},
            {1000.00000, 1000.0},
            {1000.01, 1000.01},
            {1000.1, 1000.10},
            {1000.001, 1000.0},
            {1000.005, 1000.0},
            {1000.007, 1000.01},
            {1000.999, 1001.0},
            {1000.111, 1000.11}
    });
}

@Test
public void testDecimalFormat() {
    Assert.assertEquals(expectedValue, formatDecimal(amount), 0.00);
}

Другие вопросы по теме