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

1259

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

Алекс Сперлинг
источник

Ответы:

751

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

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

curtisk
источник
9
Это, вероятно, лучшее решение, представленное на данный момент. Причина, по которой я не заметил эту возможность, когда впервые увидел класс DecimalFormat, заключается в том, что он был представлен только в Java 1.6. К сожалению, я ограничен использованием 1.5, но это будет полезно знать в будущем.
Алекс Сперлинг
1
Я попробовал это с:, "#.##"округления HALF_UP. 256.335f-> "256.33"... (пример взят из комментариев к ответу @ asterite).
большие камни
6
Пожалуйста, будьте осторожны, так как DecimalFormat зависит от вашей текущей локальной конфигурации, вы не можете получить точку в качестве разделителя. Я лично предпочитаю ответ
Asterite
1
Также помните, что вы не должны ожидать, что DecimalFormat будет поточно-ориентированным. Согласно документации Java : Десятичные форматы обычно не синхронизируются. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, он должен быть синхронизирован извне.
CGK
1
как мне сделать так, чтобы оно правильно округлялось, чтобы оно не
471

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

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

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

asterite
источник
71
ОБНОВЛЕНИЕ: Я только что подтвердил, что делать это быстрее, чем используя DecimalFormat. Я зациклился, используя DecimalFormat 200 раз, и этот метод. Для завершения 200 циклов DecimalFormat потребовалось 14 мс, этот метод занял менее 1 мс. Как я и подозревал, это быстрее. Если вам платят за такт, это то, что вы должны делать. Я удивлен, что Крис Кадмор даже сказал бы то, что он сказал, чтобы быть честным. выделение объектов всегда дороже, чем приведение примитивов и использование статических методов (Math.round (), а не decimalFormat.format ()).
Энди Джей
99
Эта техника не работает более чем в 90% случаев. -1.
Маркиз Лорн
25
Действительно, это не удается Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775.
Роберт Тупело-Шнек
54
Будьте очень осторожны при использовании этого метода (или любого округления с плавающей точкой). Это не так просто 265.335. Промежуточный результат 265,335 * 100 (с точностью до 2 цифр) составляет 26533,499999999996. Это означает, что он округляется до 265,33. При преобразовании чисел с плавающей запятой в действительные десятичные числа просто возникают проблемы. Смотрите ответ EJP здесь на stackoverflow.com/a/12684082/144578
Себастьян ван ден Брук
6
@SebastiaanvandenBroek: Ух ты, я никогда не знал, что так легко получить неправильный ответ. Однако, если кто-то работает с неточными числами, нужно признать, что любое значение не является точным . 265.335действительно означает 265.335 += tolerance, что допуск зависит от предыдущих операций и диапазона входных значений. Мы не знаем истинного, точного значения. При краевых значениях любой ответ, возможно, является правильным. Если нам нужно быть точным, мы не должны работать в два раза. failЗдесь не в преобразовании обратно в два раза. Это в OP, думая, что он может полагаться на входящее, 265.335как являющееся именно этим.
ToolmakerSteve
191
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));
    }
MetroidFan2002
источник
38
Это мое предпочтительное решение. Еще короче: BigDecimal.valueOf (doubleVar) .setScale (yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (double val) на самом деле вызывает Double.toString () под капотом;)
Этьен Неве,
4
Ницца. Не срезайте углы и используйте его, так new BigDecimal(doubleVar)как вы можете столкнуться с проблемами с округлением чисел с плавающей запятой
Edd
8
@ Как ни странно, проблема округления возникает в том случае, когда СебастианванденБрук упоминает в комментарии к ответу астериты. 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
7
@ToolmakerSteve. Это потому, что использование нового BigDecimal с двойным напрямую принимает значение double и пытается использовать его для создания BigDecimal, тогда как при использовании BigDecimal.valueOf или формы tostring сначала анализирует его в строку (более точное представление) перед преобразованием ,
MetroidFan2002
116

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

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

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

Милхаус
источник
25
Я считаю , что одна из целей этого вопроса в том , что «должно не быть никаких замыкающие нули».
Ланчбокс
7
На этот вопрос оператору не нужны нули, но это именно то, что я хотел. Если у вас есть список чисел с 3 десятичными разрядами, вы хотите, чтобы все они имели одинаковые цифры, даже если это 0.
Том Кинкейд
Вы забыли уточнитьRoundingMode.
Игорь Ганапольский
1
@IgorGanapolsky по умолчанию Decimal modeиспользуетRoundingMode.HALF_EVEN.
EndermanAPM
87

Как отметили некоторые другие, правильный ответ должен использовать либо 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
Маркиз Лорн
источник
8
Хитрость в том, что во всех ваших 9251 ошибках напечатанный результат все еще корректен.
Дидье Л
7
@DidierL Меня это не удивляет. Мне очень повезло, что я сделал «Численные методы» в качестве моего самого первого курса по вычислительной технике и познакомился в самом начале с тем, что можно и что нельзя делать с плавающей точкой. Большинство программистов довольно расплывчаты в этом.
Маркиз Лорн
15
Все, что вы делаете, это опровергаете, что плавающее не представляет точно много десятичных значений, что, я надеюсь, мы все понимаем. Не то, чтобы округление вызывало проблему. Как вы признаете, цифры по-прежнему печатаются, как и ожидалось.
Питер Лори
8
Ваш тест не пройден, возьмите round () и тест не пройдёт 94% времени. ideone.com/1y62CY печатает. 100 trials 94 errorsВы должны начать с теста, который проходит успешно , и показать, что введение округления нарушает тест.
Питер Лори
6
Опровержение опровергнуто здесь. Использование Math.round для этого диапазона doubleбез ошибок ideone.com/BVCHh3
Питер Лоури,
83

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

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

user593581
источник
2
Я думаю, что это лучшее решение для пользователей Java 1.5 (и ниже). Один комментарий, не используйте режим округления HALF_EVEN, так как он имеет разностное поведение для нечетных и четных чисел (например, 2,5 округления до 2, а 5,5 округления до 6), если только это не то, что вы хотите.
IcedDante
4
Первое решение верное: второе не работает. Смотрите здесь для доказательства.
маркиз Лорн
1
@EJP: Даже первое решение с RoundingMode.HALF_UPнеправильным. Попробуйте это с 1.505. Правильный способ заключается в использовании BigDecimal.valueOf(d).
Матиас Браун
Матиас Браун, решение в порядке, следовательно, 31 ups. 1.505 десятичное запоминается в двойных числах с плавающей запятой как 1.50499998, если вы хотите взять 1.505 и преобразовать из двойного в десятичное, то вы должны сначала преобразовать его в Double.toString (x) затем поместите его в BigDecimal (), но это очень медленно, и в первую очередь побеждает цель использовать double для скорости.
Хэмиш
1
Запустил цикл 100k с BigDecimal (заняло 225 мс) и Math.round (2 мс), и вот время ... Взятое время: 225 миллисекунд для преобразования с использованием в: 9232.13 Время взятое: 2 миллисекунды для преобразования в : 9232.13 techiesinfo.com
user1114134
59

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

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));
JibW
источник
Любая причина, почему Double.valueOf()был выбран Double.parseDouble()? valueOf()Метод возвращает Doubleобъект, в то время как parseDouble()будет возвращать doubleпримитивно. При способе написания текущего кода вы также применяете автоматическую распаковку к возврату, чтобы привести его к примитиву, ожидаемому вашей twoDoubleпеременной, дополнительной операции с байт-кодом. Я бы изменил ответ, чтобы использовать parseDouble()вместо этого.
Экброди
Double.parseDouble()нужен Stringвклад.
Райд
38

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

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
Ovesh
источник
33
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
Крис Кадмор
источник
2
да, это именно то, что math.round делает для положительных чисел, но вы пробовали это с отрицательными числами? люди используют math.round в других решениях, чтобы также покрыть случай отрицательных чисел.
Хэмиш
Примечание: Math.floor(x + 0.5)иMath.round(x)
Питер Лори
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: вы получаете два преимущества по цене одной реализации кода. ;)

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

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

  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)
Mifeet
источник
18

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

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}
Amit
источник
@mariolpantunes: это не удастся. Попробуйте это: round(1.005,2);илиround(0.50594724957626620092, 20);
Матиас Браун
Оно работает. Но неинформативно с плавающей точкой и удваивается приблизительные значения. Давайте рассмотрим ваш первый пример. Если вы напечатаете вывод заинтересованныхInZeroDPs перед Math.round, он напечатает 100.49999999999999. Вы потеряли точность как
математическую форму, округляя
двойной это быстро! десятичное медленно. компьютеры не утруждают себя обработкой своего мышления в десятичной записи. Вы должны отказаться от некоторой десятичной точности, чтобы быстрые числа с плавающей запятой удваивались.
Хэмиш
@ Hamish Вопрос в точности, а не в скорости.
маркиз Лорн
7

Попробуйте это: 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

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

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

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

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

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

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

Use BigDecimal or any other decimal-based format.

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

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

Иван
источник
6

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

 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

Демо онлайн.

Jorgesys
источник
5

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

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

}

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

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

Я согласен с выбранным ответом использовать 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
Я не сравнивал его с другими решениями, но в исходном коде доступен тест jmh: github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/… Я запустил тест на виртуальной машине результаты доступны в виде файла csv здесь: github.com/tools4j/decimal4j/wiki/Performance
marco
1
Сбой DoubleRounder в следующих случаях: DoubleRounder.round (256.025d, 2) - ожидается: 256.03, факт: 256.02 или для DoubleRounder.round (260.775d, 2) - ожидается: 260.78, факт: 260.77.
iaforek
@iaforek: это правильно, потому что DoubleRounder выполняет математически правильное округление. Однако я признаю, что это несколько нелогично и, следовательно, обновит мой ответ соответственно.
Марко
3

Фрагмент кода ниже показывает, как отображать 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

Если вы используете 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

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

Как округлить число в 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 ***
Suragch
источник
1
Вы решаете только конкретный случай округления до 0 десятичных знаков. Оригинальный вопрос более общий.
lukas84
3

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

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;
Craigo
источник
Это не удастся в случаях, когда myVal не меньше 1 и с нулями после десятичной запятой вне значения шкалы. Скажем, у вас есть myVal = 9.00000000912385; Выше вернется 9.0. Я думаю, что мы должны предоставить решение, которое работает во всех случаях myVal. Не специально для стоимости, которую вы указали.
Тавалендо
@ user102859 В вашем примере 9.0 - правильный результат. Я не понимаю, как это не получится.
Крейго
2

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

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

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

System.out.println(num); // 4.89
Джамал Уддин
источник
1

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

Ален Круз
источник
0

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

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

    double result = Math.round(value * p)/p;
цель
источник
1
Производит 1.0для value = 1.005а dp = 2. Используйте это вместо этого.
Матиас Браун
это нормально, Мэтт, твой пример недействителен. потому что 1.005 не может быть представлен в двойных числах с плавающей точкой в ​​любом случае. он должен храниться выше или ниже 1.005, т.е. при компиляции он хранится как двойное число: 1.0049998 (в скомпилированном коде оно не хранится как десятичное число, как вы могли бы поверить читателям) цель верна, он хранит значения как числа с плавающей запятой двойной, где крайние случаи, подобные твоему, в любом случае незначительны. если бы это было так, то вы бы использовали 3dp, затем конвертировали бы его в десятичную, а затем выполняли бы функцию десятичного округления, как и ссылку, которую вы опубликовали.
Хэмиш
1
@ Хэмиш Я не понимаю, где Матиас "заставил бы читателей поверить" в любую такую ​​вещь, как то, что значение составлено как десятичное число Не вкладывайте слова в уста других людей.
Маркиз Лорн
0

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

pwojnowski
источник
0

В общем, округление выполняется путем масштабирования: 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
Амр Али
источник