Як округлити число до п яти знаків у Яві


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

EDIT : Оригінальна відповідь не відповідає точності подвійних значень. Це добре, якщо вам не дуже важливо, кругляє він вгору чи вниз. Але якщо ви хочете точного округлення, тоді потрібно враховувати очікувану точність значень. Значення з плаваючою комою мають внутрішнє двійкове подання. Це означає, що таке значення, як 2.7735, насправді не має такого точного значення внутрішньо. Він може бути трохи більшим або трохи меншим. Якщо внутрішнє значення трохи менше, то воно не округлятиметься до 2,7740. Щоб виправити цю ситуацію, вам потрібно знати про точність значень, з якими ви працюєте, і додати або відняти це значення перед округленням. Наприклад, коли ви знаєте, що ваші значення точні до 6 цифр, то для округлення значень на півдорозі вгору додайте цю точність до значення:

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

Для округлення вниз відніміть точність.


9
Це, мабуть, найкраще рішення, представлене поки що. Причина, коли я не помітив цей інструмент, коли я вперше подивився на клас DecimalFormat, полягає в тому, що він був представлений лише в Java 1.6. На жаль, я обмежений у використанні 1,5, але це буде корисно знати на майбутнє.
Alex Spurling

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

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

1
Також пам’ятайте, що не слід очікувати, що DecimalFormat буде безпечним для потоків. Відповідно до документів Java : Десяткові формати, як правило, не синхронізовані. Рекомендується створювати окремі екземпляри формату для кожного потоку. Якщо декілька потоків отримують доступ до формату одночасно, він повинен бути синхронізований зовні.
CGK

1
як це зробити так, щоб він зробив правильне округлення, щоб він не округлювався від 0,0004 до 0,001

471

Припускаючи , що valueце double, ви можете зробити:

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

Це для точності 5 цифр. Кількість нулів вказує на кількість десяткових знаків.


71
ОНОВЛЕННЯ: Я щойно підтвердив, що робити це ШЛИШЕ, ніж використовувати DecimalFormat. Я циклічно використовував DecimalFormat 200 разів, і цей метод. DecimalFormat знадобився 14 мс для завершення 200 циклів, цей метод зайняв менше 1 мс. Як я підозрював, це швидше. Якщо ви отримуєте зарплату за тактовим циклом, це саме ви повинні робити. Я здивований, що Кріс Кадмор навіть сказав, що він сказав чесно. виділення об'єктів завжди дорожче, ніж лиття примітивів та використання статичних методів (Math.round () на відміну від decimalFormat.format ()).
Енді Джей

99
Ця методика виходить з ладу у понад 90% випадків. -1.
користувач207421

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Тут не в перетворенні назад в два рази. Його мислення в ОП може розраховувати на те, що входить 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));
    }

38
Це моє переважне рішення. Ще коротше: BigDecimal.valueOf (doubleVar) .setScale (yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (подвійний val) насправді викликає Double.toString () під капотом;)
Етьєн Невеу

4
Приємно. Не скорочуйте кути і використовуйте, new BigDecimal(doubleVar)оскільки ви можете зіткнутися з проблемами з округленням плаваючих точок
Едд,

8
@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

7
@ToolmakerSteve Це тому, що використання нового BigDecimal з подвійним приймає подвійне значення безпосередньо і намагається використовувати його для створення 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

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

8
Хитрість полягає в тому, що при всіх ваших помилках 9251 друкований результат все ще є правильним.
Didier L

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

15
Все, що ви робите, - це спростування того, що плаваючий не представляє точно багато знаків після коми, які, я би сподівався, ми всі розуміємо. Не те, що округлення викликає проблеми. Як ви визнаєте, цифри все ще друкуються як очікувалося.
Пітер Лорі

8
Ваш тест зламаний, вийміть раунд () і тест провалить 94% часу. ideone.com/1y62CY друкує 100 trials 94 errorsВи повинні почати з тесту, який проходить, і показати, що введення округлення порушує тест.
Пітер Лорі

6
Спростування, тут спростовано. Використання Math.round для цього діапазону doubleяк без помилок ideone.com/BVCHh3
Peter Lawrey

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


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

4
Перше рішення правильне: друге не працює. Дивіться тут для підтвердження.
користувач207421

1
@EJP: Навіть перше рішення з RoundingMode.HALF_UP- неправильне. Спробуйте 1.505. Правильний спосіб - використовувати BigDecimal.valueOf(d).
Маттіас Браун

Маттіас Браун, рішення чудово, отже, 31 підйом. потім покладіть його в BigDecimal (), але це надзвичайно повільно, і перемагає мета використання подвійного для швидкості в першу чергу.
Гаміш

1
Пробіг цикл 100k за допомогою BigDecimal (зайняв 225 мс) та Math.round (2 мс), і ось час: час, необхідний для конвертації: 225 мілілі секунди за допомогою: 9232.13 Зайнятий час: 2 мілі секунди для перетворення на : 9232.13 techiesinfo.com
користувач1114134

59

Ви можете використовувати клас 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()замість цього.
екбродів

Double.parseDouble()потребує Stringвведення.
Райд

38

Реальні Java How-to публікують це рішення, яке також сумісне для версій до Java 1.6.

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

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

2
так, це саме те, що математика робить для позитивних чисел, але ви пробували це з від'ємними числами? люди використовують math.round в інших рішеннях, щоб також висвітлити випадок негативних чисел.
хаміш

Примітка: Math.floor(x + 0.5) іMath.round(x)
Пітер Лорі

30

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

повернеться як вихід: 0.9124

[EDIT: також якщо формат каретки такий ("# 0. ############"), і ви вводите десяткове число, наприклад 3.1415926, для аргументу DecimalFormat не створює сміття ( наприклад, задні нулі) і повернеться: 3.1415926.. якщо ви так схильні. Зрозуміло, це трохи дослідний стиль, який сподобався деяким розробникам - але ей, він має низький слід пам’яті під час обробки та його дуже легко реалізувати.]

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


2
Якщо ви дійсно хочете десяткових чисел для обчислення (і не тільки для виведення), не використовуйте двійковий формат з плаваючою комою, наприклад double. Використовуйте BigDecimal або будь-який інший формат на основі десятків.
Paŭlo Ebermann

20

Ось короткий опис того, що ви можете використовувати, якщо результат бажаний як 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в результаті. Я б не рекомендував його для перетворення рядків, оскільки подвійний може не в змозі представити те, що саме ви хочете (див., Наприклад, тут ):

  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)

18

Ви можете використовувати наступний метод утиліти -

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);
Маттіас Браун

Це працює. Але неінформативно плаваючий і парний - це наближення. Розглянемо ваш перший приклад. Якщо ви надрукуєте вихід зацікавленихInZeroDP перед Math.round, він надрукує 100.49999999999999. Ви втратили точність як такий Math.round навколо нього як 100. Завдяки природі або поплавкам і подвійним ударам є випадки прикордонних
ліній,

подвійний - швидкий! десяткова повільна. комп'ютери не переймаються обробкою свого мислення у десятковій нотації. Ви повинні відмовитися від десяткової точності, щоб плаваюча крапка зберігалася вдвічі швидше.
хаміш

@hamish Питання полягає в точності, а не в швидкості.
користувач207421



7

Спробуйте це: 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;
        }
    }
}

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

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

Use BigDecimal or any other decimal-based format.

Я використовую BigDecimal для розрахунків, але майте на увазі, що це залежить від розміру чисел, з якими ви маєте справу. У більшості моїх реалізацій я вважаю, що аналіз з подвійним або цілим на 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

демо в Інтернеті.


5

Про всяк випадок, якщо комусь все-таки потрібна допомога в цьому. Це рішення прекрасно працює для мене.

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

}

повертає a String з потрібним виходом.


Будь ласка, включіть у коментарі свою причину заборони, інакше це ми називаємо залякування.
Ашер. М. O

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/… Я запустив тест на VM , результати доступні у форматі 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вибрати кілька значень перерахунку, залежно від поведінки, якої ви вимагаєте.


3

Я прийшов сюди, просто бажаючи простої відповіді про те, як округлити число. Це додаткова відповідь, щоб забезпечити це.

Як округлити число на Java

Найпоширеніший випадок - використання Math.round().

Math.round(3.7) // 4

Числа округляються до найближчого цілого числа. .5Значення округляється. Якщо вам потрібна інша поведінка округлення, ніж це, ви можете використовувати одну з інших функцій Math . Дивіться порівняння нижче.

круглий

Як було сказано вище, це округлює до найближчого цілого числа. .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

стеля

Будь-яке десяткове значення округляється до наступного цілого числа. Він іде до стелі . Цей метод повертає a 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

підлогу

Будь-яке десяткове значення округляється до наступного цілого числа. Цей метод повертає a 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значення круглі до парного числа. Цей метод повертає a 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 ***

1
ви вирішуєте лише конкретний випадок округлення до 0 десяткових знаків. Оригінальне запитання більш загальне.
lukas84

3

Якщо ви використовуєте технологію, яка має мінімальний 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

@ user102859 У вашому прикладі 9.0 - правильний результат. Я не розумію, як це не вдасться.
Craigo

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.



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
@hamish Я не бачу, де Маттіас "змусив би читачів повірити" в щось таке, як те, що значення складається як десяткове. Не кладіть слова в уста інших людей.
користувач207421

0

Майте на увазі, що 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 - як правильно перетворити подвійний на String .


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.