Округление плавающих чисел [дубликат]

Рассматривайте каждую дату по месяцам, а затем вычитайте, чтобы найти разницу.

var past_date = new Date('11/1/2014');
var current_date = new Date();

var difference = (current_date.getFullYear()*12 + current_date.getMonth()) - (past_date.getFullYear()*12 + past_date.getMonth());

Это даст вам разницу между месяцами между двумя датами, игнорируя дни.

1023
задан Taryn 22 March 2017 в 17:22
поделиться

29 ответов

Используйте 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
588
ответ дан Michael Parker 16 August 2018 в 10:48
поделиться
  • 1
    Это, вероятно, лучшее решение, представленное до сих пор. Причина, по которой я не заметил этот объект, когда я впервые посмотрел на класс DecimalFormat, заключается в том, что он был введен только в Java 1.6. К сожалению, я ограничусь использованием 1.5, но будет полезно знать будущее. – Alex Spurling 1 October 2008 в 14:07
  • 2
    Не работает с показателями decimal decimal, например format ("0.0E00"). Он будет в этом примере от 0.0155 до 0.0E00 вместо 1.6E-02. – Martin Clemens Bloch 16 October 2014 в 00:50
  • 3
    Я пробовал это с помощью: "#.##", округления HALF_UP. 256.335f - & gt; "256.33" ... (пример проистекает из комментариев к ответу @ asterite). – bigstones 11 December 2015 в 12:43
  • 4
    Пожалуйста, будьте осторожны, поскольку DecimalFormat зависит от текущей локальной конфигурации, вы можете не получить точку в качестве разделителя. Я лично предпочитаю ответ Астерита ниже – Gomino 2 March 2016 в 18:01
  • 5
    как я делаю так, чтобы он делал правильное округление, поэтому он не будет округлен от 0,0004 до 0,001 – user 20 October 2016 в 08:00

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

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

    double result = Math.round(value * p)/p;
0
ответ дан aim 16 August 2018 в 10:48
поделиться
  • 1
    Производит 1.0 для value = 1.005 и dp = 2. Используйте этот . – Matthias Braun 19 May 2014 в 15:34
  • 2
    это нормально. Мэтт, ваш пример недействителен. потому что 1.005 не может быть представлено в double-double с плавающей запятой. он должен храниться на самом деле выше или ниже 1.005, т. е. он хранится как двойной при компиляции: 1.0049998 (он не сохраняется как десятичный код в вашем скомпилированном коде, как вы бы считали читателей) цель правильная, он сохраняет значения как с плавающей запятой double, где случаи бахромы, подобные вашим, в любом случае несущественны. если бы это было так, то вы бы использовали 3dp, а затем преобразовали его в десятичную, а затем выполнили десятичную круглую функцию, как и ссылку, которую вы опубликовали. – hamish 13 July 2014 в 09:15
  • 3
    @hamish Я не вижу, где Маттиас «читал бы читателей», что-то вроде того, что значение скомпилировано как десятичное. Не кладите слова в уши других людей. – user207421 28 June 2015 в 13:02

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

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}
17
ответ дан Amit 16 August 2018 в 10:48
поделиться
  • 1
    Не работает, см. здесь для доказательства. – user207421 20 March 2013 в 01:18
  • 2
    Почему вы говорите, что это не работает? Это сработало для меня. – mariolpantunes 5 February 2014 в 02:55
  • 3
    @mariolpantunes: Он потерпит неудачу. Попробуйте следующее: round(1.005,2); или round(0.50594724957626620092, 20); – Matthias Braun 29 March 2014 в 03:02
  • 4
    Оно работает. Но неинформативно плавающие и двойные числа являются приближениями. Рассмотрим ваш первый пример. Если вы печатаете вывод заинтересованныхInZeroDPs перед Math.round, он будет печатать 100.49999999999999. Вы потеряли точность как таковую Math.round вокруг нее как 100. Из-за природы или поплавков и удвоений есть пограничные случаи, когда она не работает должным образом (подробнее здесь ru.wikipedia.org/wiki/Floating_point#Accuracy_problems ) – mariolpantunes 31 March 2014 в 10:04
  • 5
    double - это быстро! десятичное медленное. компьютеры не перерабатывают свое мышление в десятичной нотации. вы должны отказаться от некоторой десятичной точности, чтобы поддерживать плавающую точку с двойной скоростью. – hamish 13 July 2014 в 10:16

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

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

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

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

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

}
6
ответ дан Andrei Lupsa 16 August 2018 в 10:48
поделиться

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

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

}

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

5
ответ дан Asher 16 August 2018 в 10:48
поделиться

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

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

72
ответ дан Basil Bourque 16 August 2018 в 10:48
поделиться
  • 1
    Я думаю, что это лучшее решение для пользователей Java 1.5 (и ниже). Один комментарий tho, не используйте режим округления HALF_EVEN, поскольку он имеет поведение diff для нечетных и четных чисел (например, 2.5 раунда до 2, в то время как 5.5 раундов до 6), если это не то, что вы хотите. – IcedDante 16 July 2012 в 20:11
  • 2
    Первое решение правильное: второе не работает. См. здесь для доказательства. – user207421 20 March 2013 в 01:27
  • 3
    @EJP: Даже первое решение с RoundingMode.HALF_UP неверно. Попробуйте с помощью 1.505. Правильный способ - использовать BigDecimal.valueOf(d). – Matthias Braun 29 March 2014 в 02:58
  • 4
    Маттиас Браун, решение прекрасное, следовательно, 31 взнос .. 1.505 десятичный символ хранится в спящей точке double как 1.50499998, если вы хотите взять 1.505 и конвертировать из double в десятичный, тогда вам нужно сначала преобразовать его в Double.toString (x) затем поместите его в BigDecimal (), но это очень медленно, и в первую очередь побеждает цель использования double для скорости. – hamish 13 July 2014 в 09:22
  • 5
    Ran цикл 100k с BigDecimal (взял 225 мс) и Math.round (2 мс), и вот время ... Время взято: 225 миллисекунд, чтобы конвертировать, используя: 9232.13 Время: 2 миллисекунды, чтобы конвертировать в : 9232.13 techiesinfo.com – user1114134 31 July 2014 в 20:32
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
30
ответ дан Chris Cudmore 16 August 2018 в 10:48
поделиться
  • 1
    да, это именно то, что math.round делает для положительных чисел, но вы пробовали это с отрицательными номерами? люди используют математику в других решениях, чтобы также охватывать случай отрицательных чисел. – hamish 13 July 2014 в 09:39
  • 2
    Примечание: Math.floor(x + 0.5) и Math.round(x) – Peter Lawrey 2 February 2018 в 12:25

Ниже приведен фрагмент кода, показывающий, как отображать 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
ответ дан Chris Gerken 16 August 2018 в 10:48
поделиться

Вот краткое изложение того, что вы можете использовать, если хотите, чтобы результат был как 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)
    
15
ответ дан Community 16 August 2018 в 10:48
поделиться

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

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

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

398
ответ дан confile 16 August 2018 в 10:48
поделиться
  • 1
    UPDATE: Я только что подтвердил, что делать это WAY быстрее, чем использовать DecimalFormat. Я зацикливал, используя DecimalFormat 200 раз, и этот метод. DecimalFormat занял 14 мс, чтобы завершить 200 циклов, этот метод занял менее 1 мс. Как я подозревал, это происходит быстрее. Если вам платят часы, это то, что вы должны делать. Я удивлен, что Крис Кудмор даже скажет, что он сказал честно. выделение объектов всегда дороже, чем приведение примитивов и использование статических методов (Math.round (), а не decimalFormat.format ()). – Andi Jay 10 July 2012 в 15:35
  • 2
    Этот метод выходит из строя более чем в 90% случаев. -1. – user207421 2 October 2012 в 04:12
  • 3
    Действительно, это не удается: Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775. – Robert Tupelo-Schneck 3 October 2012 в 19:51
  • 4
    @ RobertTupelo-Schneck Это не работает, потому что double имеет точность примерно 14 знаков после запятой (что-то большее, чем это будет неточно) – sambe 19 August 2013 в 15:18
  • 5
    Будьте очень осторожны при использовании этого метода (или любого округления плавающих точек). Это не так просто, как 265.335. Промежуточный результат 265.335 * 100 (точность 2 цифры) составляет 26533.499999999996. Это означает, что он округляется до 265,33. Там просто присущи проблемы при преобразовании чисел чисел с плавающей запятой в реальные десятичные числа. См. Ответ EJP здесь, stackoverflow.com/a/12684082/144578 – Sebastiaan van den Broek 14 November 2013 в 15:51

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

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;
0
ответ дан Craigo 16 August 2018 в 10:48
поделиться

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

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

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

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

4
ответ дан Drew Noakes 16 August 2018 в 10:48
поделиться

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

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

8
ответ дан Easwaramoorthy K 16 August 2018 в 10:48
поделиться

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

Use BigDecimal or any other decimal-based format.

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

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

5
ответ дан Harshit Agrawal 16 August 2018 в 10:48
поделиться

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

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));
52
ответ дан JibW 16 August 2018 в 10:48
поделиться
  • 1
    Любая причина, по которой Double.valueOf() была выбрана над Double.parseDouble()? Метод valueOf() возвращает объект Double, а parseDouble() возвращает примитив double. С учетом того, как написан текущий код, вы также применяете автоматическое распаковывание к возврату, чтобы передать его в примитив, который ожидает ваша переменная twoDouble, дополнительную операцию байт-кода. Я бы изменил ответ, чтобы использовать parseDouble(). – ecbrodie 3 December 2015 в 07:25
  • 2
    Double.parseDouble() требуется вход String. – Ryde 5 May 2016 в 23:40

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

 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

demo online.

2
ответ дан Jorgesys 16 August 2018 в 10:48
поделиться

Попробуйте это: 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
ответ дан Li Ying 16 August 2018 в 10:48
поделиться

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

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

8
ответ дан MAbraham1 16 August 2018 в 10:48
поделиться

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

3
ответ дан marco 16 August 2018 в 10:48
поделиться
  • 1
    У вас есть показатели, показывающие, насколько эффективно ваше решение по сравнению с другими? – iaforek 19 May 2017 в 14:26
  • 2
    Я не сравнивал его с другими решениями, но в исходном коде есть метка jmh: ​​ github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/… Я выполнил бенчмарк на виртуальной машине, результаты доступны как csv-файл здесь: github.com/tools4j/decimal4j/wiki/Performance – marco 19 May 2017 в 22:23
  • 3
    DoubleRounder не подходит для следующих случаев: DoubleRounder.round (256.025d, 2) - ожидается: 256.03, актуально: 256.02 или для DoubleRounder.round (260.775d, 2) - ожидается: 260.78, актуально: 260.77. – iaforek 1 June 2017 в 22:46
  • 4
    @iaforek: это правильно, потому что DoubleRounder выполняет математически правильное округление. Однако я признаю, что это несколько противоречиво и, соответственно, обновит мой ответ. – marco 11 June 2017 в 23:45

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

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

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

98
ответ дан Milhous 16 August 2018 в 10:48
поделиться
  • 1
    Я считаю, что одна из целей вопроса заключалась в том, что «должно быть не быть любым конечным нулем». – Lunchbox 20 November 2012 в 22:10
  • 2
    Для этого вопроса op не хотел нулей, но это именно то, что я хотел. Если у вас есть список чисел с 3 десятичными знаками, вы хотите, чтобы все они имели одинаковые цифры, даже если это 0. – Tom Kincaid 8 April 2014 в 18:06
  • 3
    Вы забыли указать RoundingMode. – Igor Ganapolsky 29 September 2015 в 21:27
  • 4
    @IgorGanapolsky по умолчанию Decimal mode использует RoundingMode.HALF_EVEN. – EndermanAPM 27 September 2016 в 15:40
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));
    }
164
ответ дан Nav 16 August 2018 в 10:48
поделиться
  • 1
    Это мое предпочтительное решение. Еще короче: BigDecimal.valueOf (doubleVar) .setScale (yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (double val) на самом деле вызывает Double.toString () под капотом;) – Etienne Neveu 9 February 2010 в 11:59
  • 2
    Ницца. Не разрезайте углы и используйте new BigDecimal(doubleVar), так как вы можете столкнуться с проблемами с округлением плавающих точек – Edd 26 January 2015 в 18:57
  • 3
    @Edd, интересно, проблема округления возникает в случае, когда SebastiaanvandenBroek упоминает в комментарии к ответу астерита. double val = 265.335;, BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString(); = & gt; 265.34, но (new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString(); = & gt; 265.33. – ToolmakerSteve 28 August 2015 в 03:41
  • 4
    @ToolmakerSteve Это связано с тем, что использование нового BigDecimal с двойным значением принимает двойное значение и пытается использовать это для создания BigDecimal, тогда как при использовании BigDecimal.valueOf или в форме tostring сначала анализируется строка (более точное представление) перед преобразованием , – MetroidFan2002 30 August 2015 в 05:09

Java Java Real-How [to] отправляет это решение, которое также совместимо для версий до Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
34
ответ дан Ovesh 16 August 2018 в 10:48
поделиться

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

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

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

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

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

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

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);

будет return в качестве выхода: 0.9124

[EDIT: также, если формат каретки похож на («# 0. ############»), и вы вводите десятичную, например 3.1415926, для аргументации, DecimalFormat не производит никакого мусора (например, конечных нулей) и вернет: 3.1415926 .. если вы так наклонены. Конечно, это немного многословно для симпатии некоторых разработчиков, но эй, у него низкий объем памяти во время обработки и очень легко реализуется.]

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

27
ответ дан Paŭlo Ebermann 16 August 2018 в 10:48
поделиться
  • 1
    Если вам действительно нужны десятичные числа для вычисления (и не только для вывода), не используют формат с плавающей запятой, основанный на двоичном формате , такой как double. Используйте BigDecimal или любой другой десятичный формат. – Paŭlo Ebermann 3 July 2011 в 20:57

Имейте в виду, что String.format () и DecimalFormat производят строку, используя стандартный язык Locale. Таким образом, они могут писать форматированное число с точкой или запятой в качестве разделителя между целыми и десятичными частями. Чтобы убедиться, что округленная строка находится в формате, который вы хотите использовать 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 .

1
ответ дан pwojnowski 16 August 2018 в 10:48
поделиться

Если вы рассматриваете 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 это можно решить с помощью петлевой и рекурсивной функции.

0
ответ дан Qamar 16 August 2018 в 10:48
поделиться

Как правило, округление выполняется путем масштабирования: 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
-1
ответ дан Radiodef 16 August 2018 в 10:48
поделиться

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

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

ИЛИ

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

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

2
ответ дан Se Song 16 August 2018 в 10:48
поделиться

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

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

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

Math.round(3.7) // 4

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

round

Как указано выше, это округляет до ближайшего целого числа. .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

ceil

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

floor

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

rint

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

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

Math.rint(-3.0); // -3.0
Math.rint(-3.1); // -3.0
Math.rint(-3.5); // -4.0 ***
Math.rint(-3.9); // -4.0
Math.rint(-4.5); // -4.0 ***
Math.rint(-5.5); // -6.0 ***
3
ответ дан Suragch 16 August 2018 в 10:48
поделиться
  • 1
    Я получил нижний план, но я не уверен, что не так, поэтому я не знаю, что изменить. – Suragch 4 December 2017 в 00:33

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

EDIT: Чтобы ответить на некоторые комментарии ниже, я отредактирую часть модуля тестового цикла с помощью 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
75
ответ дан user207421 16 August 2018 в 10:48
поделиться
  • 1
    Фокус в том, что во всех ваших ошибках 9251 печатный результат все еще правильный. – Didier L 9 August 2013 в 08:52
  • 2
    @DidierL Меня это не удивляет. Мне очень повезло, что я делаю «Численные методы» в качестве своего первого вычислительного курса и в самом начале вводится то, что с плавающей точкой можно и не может сделать. Большинство программистов довольно расплывчато об этом. – user207421 13 August 2013 в 10:55
  • 3
    Все, что вы делаете, - это опровержение того, что плавающее значение не равно много десятичных значений, что я надеюсь, что мы все поймем. Не то, чтобы округление действительно вызывает проблему. Как вы признаете, цифры по-прежнему печатаются, как ожидалось. – Peter Lawrey 2 March 2014 в 13:21
  • 4
    Ваш тест сломан, оберните (), и тест не проходит 94% времени. ideone.com/1y62CY prints 100 trials 94 errors Вы должны начать с теста, который проходит, и показать, что введение округления прерывает тест. – Peter Lawrey 2 March 2014 в 13:26
  • 5
    Опровержение, опровергнутое здесь. Использование Math.round для этого диапазона double без ошибок ideone.com/BVCHh3 – Peter Lawrey 2 March 2014 в 13:42
Другие вопросы по тегам:

Похожие вопросы: