Двойное значение для округления в Java

У меня есть двойное значение = 1.068879335, я хочу округлить его только с двумя десятичными значениями, такими как 1,07.

я пробовал вот так

DecimalFormat df=new DecimalFormat("0.00");
String formate = df.format(value);
double finalValue = Double.parseDouble(formate) ;

это дает мне следующее исключение

java.lang.NumberFormatException: For input string: "1,07"
     at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224)
     at java.lang.Double.parseDouble(Double.java:510)

может кто-нибудь сказать мне, что не так с моим кодом.

наконец, мне нужно finalValue = 1.07;


person jimmy    schedule 25.01.2011    source источник
comment
если вы округлите это, 1.085879335, 2-значная точность, это будет 1,09 и 1,08, если округлить вниз. почему вы хотите получить 1.07 из 1.085879335?   -  person fmucar    schedule 25.01.2011


Ответы (9)


Обратите внимание на запятую в вашей строке: "1,07". DecimalFormat использует строку-разделитель для конкретной локали, а Double.parseDouble() — нет. Поскольку вы живете в стране, где десятичным разделителем является «,», вы не можете разобрать свой номер обратно.

Однако вы можете использовать тот же DecimalFormat для обратного анализа:

DecimalFormat df=new DecimalFormat("0.00");
String formate = df.format(value); 
double finalValue = (Double)df.parse(formate) ;

Но вы действительно должны сделать это вместо этого:

double finalValue = Math.round( value * 100.0 ) / 100.0;

Примечание. Как уже было сказано, числа с плавающей запятой следует использовать только в том случае, если вам не нужен точный контроль над точностью. (Финансовые расчеты являются основным примером того, когда не их использовать.)

person biziclop    schedule 25.01.2011
comment
+1 за фактическое выявление проблемы. Вы также можете явно указать DecimalFormat использовать локаль C. - person DJClayworth; 25.01.2011
comment
Как я указал в своем ответе, этот алгоритм обычно не работает. И пока Java придерживается плавающей запятой IEEE, такого алгоритма не существует. - person Waldheinz; 26.01.2011
comment
@Waldheinz Учитывая контекст вопроса, мой ответ правильный. Честная игра для вас, если вы думаете еще на один шаг, но должен ли я знать, что понизил ваш ответ, потому что вы совершенно не смогли определить, почему код не сработал в первую очередь? Конечно, я не должен. - person biziclop; 26.01.2011
comment
Извините, не хотел обидеть. Просто хотел отметить, что даже исправленный код не работает для каждого ввода. Но тогда вы ответили на исходный вопрос. - person Waldheinz; 26.01.2011
comment
@Waldheinz Все в порядке, я просто подумал, что нет необходимости редактировать мой ответ с этой важной ошибкой, потому что вы очень хорошо это объяснили. В следующий раз я не буду таким ленивым. :) - person biziclop; 26.01.2011
comment
@biziclop спасибо за этот ответ, я пытаюсь сделать что-то очень похожее на этот вопрос. stackoverflow.com/ вопросы/33692331/. Когда я добавляю 51073+51073, я получаю 153219,0 вместо 102 146. - person ; 13.11.2015
comment
@biziclop Привет, я хочу округлить вот так, если десятичное значение равно 453,65, оно округляется до 454, как это сделать - person sameer joshi; 02.03.2016

Живое решение @Sergey, но с целочисленным делением.

double value = 23.8764367843;
double rounded = (double) Math.round(value * 100) / 100;
System.out.println(value +" rounded is "+ rounded);

отпечатки

23.8764367843 rounded is 23.88

РЕДАКТИРОВАТЬ: Как указывает Сергей, не должно быть разницы между умножением double*int и double*double и делением double/int и double/double. Я не могу найти пример, где результат отличается. Однако в x86/x64 и других системах есть специальная инструкция машинного кода для смешанных значений double,int, которые, как я полагаю, использует JVM.

for (int j = 0; j < 11; j++) {
    long start = System.nanoTime();
    for (double i = 1; i < 1e6; i *= 1.0000001) {
        double rounded = (double) Math.round(i * 100) / 100;
    }
    long time = System.nanoTime() - start;
    System.out.printf("double,int operations %,d%n", time);
}
for (int j = 0; j < 11; j++) {
    long start = System.nanoTime();
    for (double i = 1; i < 1e6; i *= 1.0000001) {
        double rounded = (double) Math.round(i * 100.0) / 100.0;
    }
    long time = System.nanoTime() - start;
    System.out.printf("double,double operations %,d%n", time);
}

Отпечатки

double,int operations 613,552,212
double,int operations 661,823,569
double,int operations 659,398,960
double,int operations 659,343,506
double,int operations 653,851,816
double,int operations 645,317,212
double,int operations 647,765,219
double,int operations 655,101,137
double,int operations 657,407,715
double,int operations 654,858,858
double,int operations 648,702,279
double,double operations 1,178,561,102
double,double operations 1,187,694,386
double,double operations 1,184,338,024
double,double operations 1,178,556,353
double,double operations 1,176,622,937
double,double operations 1,169,324,313
double,double operations 1,173,162,162
double,double operations 1,169,027,348
double,double operations 1,175,080,353
double,double operations 1,182,830,988
double,double operations 1,185,028,544
person Peter Lawrey    schedule 25.01.2011
comment
Где целочисленное деление? Оператор приведения имеет более высокий приоритет, поэтому вы делите двойное число на целое число, но в этом случае целое число повышается до двойного. Так что это по сути то же самое, что и мой код. Или то же, что и Math.round(value * 100) / 100.0. Это просто вопрос стиля. Если бы вы использовали настоящее целочисленное деление, в результате вы получили бы 23. - person Sergei Tachenov; 26.01.2011
comment
@Сергей, Теоретически, вы правы, и, возможно, мой ответ специфичен для JVM, подробности см. В моем редактировании. - person Peter Lawrey; 26.01.2011
comment
Потрясающие. В JLS четко указано, что целое число должно быть повышено до удвоения в таком делении, поэтому результат должен быть одинаковым для обоих случаев в любой соответствующей реализации Java, но я понятия не имел, что JVM оптимизирует реальную реализацию таким образом. - person Sergei Tachenov; 26.01.2011
comment
вы должны ВСЕГДА явно устанавливать собственный тип данных рядом с вашими статическими числами в исходном коде и не оставлять компилятору решать за вас. ты? Я надеюсь, что нет. это важно при написании высокопроизводительных игр. - person hamish; 13.07.2014

Проблема в том, что вы используете средство форматирования локализации, которое генерирует десятичную точку для конкретной локали, в вашем случае это «,». Но Double.parseDouble() ожидает нелокализованный двойной литерал. Вы можете решить свою проблему, используя метод синтаксического анализа для конкретной локали или изменив локаль вашего средства форматирования на что-то, что использует "." как десятичная точка. Или, что еще лучше, избегайте ненужного форматирования, используя что-то вроде этого:

double rounded = (double) Math.round(value * 100.0) / 100.0;
person Sergei Tachenov    schedule 25.01.2011
comment
Как насчет того, чтобы получить 23,8764367843 => 23,88000000002? Я думаю, что обработка строк лучше. - person Manidip Sengupta; 25.01.2011
comment
@Manidip, ну, вы не можете получить точно 23,88, анализируя 23,88 как двойное, поскольку 23,88 в любом случае не может быть точно представлено в двоичном формате. Если требуется точное представление, следует использовать какую-либо библиотеку произвольной точности. - person Sergei Tachenov; 26.01.2011
comment
Вы правы, но я думал, что мы говорим о текстовом и приблизительном представлении, а не о бинарном. Вальдхайнц и Питер ответили на это выше. DecimalFormat — хороший выбор, но если он не работает, я бы попробовал String roundedStr = String.valueOf (rounded); int dotPoint = roundedStr.indexOf(.); return (roundedStr).substring(0,dotPoint ‹ 0 ? roundedStr.length() : dotPoint+2); Это не совсем правильно, вам, возможно, придется добавить отступы 0 справа, если справа от десятичной точки в roundedStr есть только 1 цифра, но вы поняли, о чем я комментировал ранее. - person Manidip Sengupta; 26.01.2011
comment
@Manidip, если нужно текстовое представление, то это, конечно, другая история. Но тогда не было бы необходимости разбирать его обратно, я думаю. В конце концов, это, конечно, зависит от того, как значение будет использоваться. - person Sergei Tachenov; 26.01.2011

Есть что-то принципиально неправильное в том, что вы пытаетесь сделать. Двоичные значения с плавающей запятой не имеют десятичные разряды. Вы не можете осмысленно округлить единицу до заданного числа знаков после запятой, потому что большинство «круглых» десятичных значений просто не могут быть представлены в виде двоичной дроби. Вот почему никогда не следует использовать float или double для обозначения денег.

Поэтому, если вы хотите, чтобы в вашем результате были десятичные разряды, этот результат должен быть либо String (который вы уже получили с помощью DecimalFormat), либо BigDecimal (который имеет метод setScale(), который делает именно то, что вы хотите). В противном случае результат не может быть таким, каким вы хотите его видеть.

Прочтите Руководство по операциям с плавающей запятой для получения дополнительной информации.

person Michael Borgwardt    schedule 25.01.2011
comment
Можем ли мы представить, что он запросил число с плавающей запятой, ближайшее к значению, округленному до двух знаков после запятой? - person DJClayworth; 26.01.2011
comment
@DJClayworth: где это может быть полезно? - person Michael Borgwardt; 26.01.2011

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

См.: 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;
        }
    }
}
person Li Ying    schedule 20.02.2014

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

Пример приведен, чтобы вы поняли, что я только что сказал.

double decimalnumber = 100.2397;
DecimalFormat dnf = new DecimalFormat( "#,###,###,##0.00" );
double roundednumber = new Double(dnf.format(decimalnumber)).doubleValue();
person Markku    schedule 25.01.2011

Это невозможно требуемым способом, потому что существуют числа с двумя десятичными знаками, которые не могут быть точно выражены с использованием чисел с плавающей запятой IEEE (например, 1/10 = 0,1 не может быть выражено как Double или Float). Форматирование всегда должно выполняться как последний шаг перед представлением результата пользователю.

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

person Waldheinz    schedule 25.01.2011
comment
Хороший вопрос, если это действительно денежный расчет, то с плавающей запятой это вообще неправильный тип данных. - person biziclop; 25.01.2011
comment
Не могли бы вы объяснить пример? Почему нельзя представить 1/10 как 0,10? - person Manidip Sengupta; 25.01.2011
comment
1/10 равно 0,10, но числа с плавающей запятой двоичные, не существует таких вещей, как 1/10 или 0,1. У вас есть только 1/2, 1/4, 1/8 и т. д., и любая их комбинация приблизительно равна 0,1. - person Peter Lawrey; 25.01.2011
comment
@Manidip Извините, я был в отключке, поэтому не смог ответить на этот вопрос своевременно. Но вмешался добрый Питер. :-) - person Waldheinz; 26.01.2011
comment
Аааа, хорошо, вы говорите о точности, теперь я понял. - person Manidip Sengupta; 26.01.2011

Вы можете использовать такой формат, как здесь,

  public static double getDoubleValue(String value,int digit){
    if(value==null){
        value="0";
     }
    double i=0;
     try {
         DecimalFormat digitformat = new DecimalFormat("#.##");
         digitformat.setMaximumFractionDigits(digit);
        return Double.valueOf(digitformat.format(Double.parseDouble(value)));

    } catch (NumberFormatException numberFormatExp) {
        return i;   
    }
}
person Knowledge Serve    schedule 18.11.2013

Если вы не хотите использовать DecimalFormat (например, из-за его эффективности) и вам нужно общее решение, вы также можете попробовать этот метод, который использует масштабированное округление:

public static double roundToDigits(double value, int digitCount) {
    if (digitCount < 0)
        throw new IllegalArgumentException("Digit count must be positive for rounding!");

    double factor = Math.pow(10, digitCount);
    return (double)(Math.round(value * factor)) / factor;
}
person Keplerian    schedule 09.04.2019