Як добре відформатувати плаваючі числа до String без зайвих десятків 0?


496

64-бітний подвійний може представляти ціле число +/- 2 53 точно

Враховуючи цей факт, я обираю використовувати подвійний тип як єдиний тип для всіх моїх типів, оскільки моє найбільше ціле число не підписане 32-бітним.

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

Тож як я можу друкувати ці подвійні файли на Java?

я намагався String.format("%f", value) , що близько, за винятком того, що я отримую багато нульових значень для малих значень.

Ось приклад виходу %f

232.00000000
0,18000000000
1237875192.0
4,5800000000
0,00000000
1.23450000

Що я хочу:

232
0,18
1237875192
4,58
0
1.2345

Звичайно, я можу написати функцію, щоб обрізати ці нулі, але це велика втрата продуктивності через маніпуляції з String. Чи можу я краще зробити інший формат коду?

EDIT

Відповіді Тома Е. та Джеремі С. неприйнятні, оскільки вони обидва довільно обирають до 2 знаків після коми. Будь ласка, зрозумійте проблему, перш ніж відповісти.

EDIT 2

Зверніть увагу, що String.format(format, args...)це залежить від місцевості (див. Відповіді нижче).


Якщо все, що ви хочете, це цілі числа, чому б не використовувати довге? Ви отримуєте більше удару на 2 ^ 63-1, ніякого незручного форматування та кращої продуктивності.
— basszero

14
Оскільки деякі значення насправді
— подвоєні

2
У деяких випадках , коли ця проблема виникла помилка була виправлена в JDK 7: stackoverflow.com/questions/7564525 / ...
— Pyrolistical

Це тільки я чи JavaScript на 100% кращий за кількістю перетворень рядків, ніж Java?
— Енді

System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
— Шаян Амані

Відповіді:


399

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

public static String fmt(double d)
{
    if(d == (long) d)
        return String.format("%d",(long)d);
    else
        return String.format("%s",d);
}

Виробляє:

232
0.18
1237875192
4.58
0
1.2345

І не покладається на струнні маніпуляції.


9
Домовились, це погана відповідь, не використовуйте. Він не працює з doubleбільшим за максимальне intзначення. Навіть при цьому longвона все-таки провалиться для величезної кількості. Далі він поверне String в експоненціальній формі, наприклад, "1.0E10", для великих значень, що, мабуть, не те, що хоче запитувач. Використовуйте %fзамість %sдругого рядка формату, щоб виправити це.
— jlh

26
ОП заявила прямо, що вони не хочуть, щоб вихід був відформатований за допомогою %f. Відповідь специфічна для описаної ситуації та бажаного результату. ОП запропонувало, що їх максимальне значення - це 32-розрядний неподписаний int, який я вважав intприйнятним (без підпису фактично не існує Java, а приклад не є проблематичним), але зміна intна longтривіальне виправлення, якщо ситуація відрізняється.
— JasonD

1
Де в запитанні сказано, що не слід цього робити?
— JasonD

6
String.format("%s",d)??? Поговоріть про зайві накладні витрати. Використовуйте Double.toString(d). Те ж саме для іншого: Long.toString((long)d).
— Андреас

15
Проблема полягає в тому, що %sце не працює з Locales. Німецькою мовою ми використовуємо "," замість "." у десяткових числах. Поки String.format(Locale.GERMAN, "%f", 1.5)повертається "1500000", String.format(Locale.GERMAN, "%s", 1.5)повертається "1,5" - з ".", Що є німецькою мовою. Чи існує також залежна від локальної версії "% s" версія?
— Фелікс Едельманн

414
new DecimalFormat("#.##").format(1.199); //"1.2"

Як зазначалося в коментарях, це не правильна відповідь на початкове запитання.
Однак, це дуже корисний спосіб форматування чисел без зайвих задніх нулів.


16
Тут важлива примітка, що 1.1 було б правильно відформатовано як "1.1" без будь-яких зворотних нулів.
— Стів Померой

53
І якщо вам потрібно певну кількість кінцевих нулів (наприклад, якщо ви друкуєте грошові суми), ви можете використовувати "0" замість "#" (тобто новий формат DecimalFormat ("0,00"). Формат (кількість); не те, що хотіла ОП, але може бути корисною для ознайомлення.
— TJ Ellis

22
Так, як оригінальний автор запитання, це відповідь WRONG. Смішно, скільки голосів налічується. Проблема цього рішення полягає в тому, що він довільно округляє до 2 десяткових знаків.
— Піролістичний

11
@Mazyod, тому що ви завжди можете передати плаваючу вказівку з більшою кількістю десяткових знаків, ніж формат. Це написання коду, який працюватиме більшу частину часу, але не охоплюватиме всі крайні випадки.
— Піролістичний

15
@Pyrolistic - IMHO, є багато оновлень, оскільки, хоча це неправильне рішення для вас, це правильне рішення для 99% + тих, хто вважає це запитання: зазвичай останні кілька цифр дублу - це "шум", які захаращують вихід, заважаючи читанні. Отже, програміст визначає, скільки цифр вигідні людині, що читає вихід, і визначає, що багато. Поширена ситуація, коли накопичені невеликі математичні помилки, тому значення може бути 12,000000034, але вважатимуть за краще округлення до 12 та відображення компактно як "12". І "12.340000056" => "12.34".
— ToolmakerSteve

226
String.format("%.2f", value) ;

13
Це правильно, але завжди друкується проміжні нулі, навіть якщо немає дробової частини. String.format ("%. 2f, 1.0005) друкує 1.00, а не 1. Чи є специфікатор формату, щоб не надрукувати дробову частину, якщо її немає?
— Emre Yazici

87
Вниз проголосували, оскільки питання просить зняти всі проміжні нулі, і ця відповідь завжди залишатиме дві плаваючі точки незалежно від нуля.
— Зулаксія

DecimalFormat був приємним трюком - хоча я в кінцевому підсумку використовував цей для моєї ситуації (таймер рівня гри), оскільки кращі нулі виглядали краще.
— Тімоті Лі Рассел

2
Я думаю, ви можете правильно обробити проміжні нулі, використовуючи g замість f.
— Пітер Айтай

3
Я використовував це рішення у виробничій системі з «% .5f», і це дійсно дуже погано, не використовувати його ... тому що він надрукував: 5.12E-4 замість 0.000512
— Хеміш

87

Коротко:

Якщо ви хочете позбутися від нульових нулів та проблем з локальним розташуванням, тоді вам слід скористатися:

double myValue = 0.00000021d;

DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(340); //340 = DecimalFormat.DOUBLE_FRACTION_DIGITS

System.out.println(df.format(myValue)); //output: 0.00000021

Пояснення:

Чому інші відповіді мене не влаштовували:

  • Double.toString()або System.out.printlnабо FloatingDecimal.toJavaFormatStringвикористовує наукові позначення, якщо подвійне менше 10 ^ -3 або більше або дорівнює 10 ^ 7

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • з використанням %fдесяткової точності за замовчуванням дорівнює 6, інакше ви можете жорстко кодувати її, але це призводить до додаткових нулів, якщо у вас менше десяткових знаків. Приклад:

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • використовуючи setMaximumFractionDigits(0);або %.0fвидаляйте будь-яку десяткову точність, що добре для цілих чисел / довгих, але не для подвійної

    double myValue = 0.00000021d;
    System.out.println(String.format("%.0f", myvalue)); //output: 0
    DecimalFormat df = new DecimalFormat("0");
    System.out.println(df.format(myValue)); //output: 0
  • використовуючи DecimalFormat, ви залежите від місцевого значення. У французькій мові десятковий роздільник є комою, а не крапкою:

    double myValue = 0.00000021d;
    DecimalFormat df = new DecimalFormat("0");
    df.setMaximumFractionDigits(340);
    System.out.println(df.format(myvalue));//output: 0,00000021

    Використання мови англійською мовою гарантує отримання точки для десяткового роздільника, де б ваша програма не працювала

Навіщо використовувати 340 тоді для setMaximumFractionDigits?

Дві причини:

  • setMaximumFractionDigitsприймає ціле число, але його реалізація має максимально допустимі цифри, DecimalFormat.DOUBLE_FRACTION_DIGITSщо дорівнює 340
  • Double.MIN_VALUE = 4.9E-324 тож, маючи 340 цифр, ви впевнені, що не обходите вашу подвійну та вільну точність

Це не працює для цілих чисел, наприклад, "2" стає "2."
— кап

Дякую, я виправив відповідь, використовуючи шаблон, 0а не шаблон#.
— JBE

Ви не використовуєте константу, DecimalFormat.DOUBLE_FRACTION_DIGITSале використовуєте значення 340, яке ви надаєте коментар, щоб показати, що воно дорівнює DecimalFormat.DOUBLE_FRACTION_DIGITS. Чому б просто не використовувати константу ???
— Maarten Bodewes

1
Оскільки цей атрибут не є загальнодоступним ... він є "зручним для пакетів"
— JBE

4
Дякую! Насправді ця відповідь є єдиною, яка дійсно відповідає всім вимогам, зазначеним у питанні, - вона не показує зайвих нулів, не округляє числа та не залежить від локальної точки. Чудово!
— Фелікс Едельман

26

Чому ні:

if (d % 1.0 != 0)
    return String.format("%s", d);
else
    return String.format("%.0f",d);

Це повинно працювати з крайніми значеннями, підтримуваними Double. Врожайність:

0.12
12
12.144252
0

2
Я віддаю перевагу цій відповіді, за допомогою якої нам не потрібно робити перетворення типів.
— Джефф Т.

Коротке пояснення: в "%s"основному дзвінки, d.toString()але це не працює intабо якщо d==null!
— Неф

24

На моїй машині наступна функція приблизно в 7 разів швидша за функцію, яку надає відповідь JasonD , оскільки вона уникає String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}

1
Гм, це не враховує місцеві місця, але і JasonD.
— TWiStErRob


11

Ні, неважливо.

Втрати продуктивності через маніпуляції з рядками дорівнюють нулю

І ось код для обрізання кінця після %f

private static String trimTrailingZeros(String number) {
    if(!number.contains(".")) {
        return number;
    }

    return number.replaceAll("\\.?0*$", "");
}

7
Я прихильнився тому, що ваше рішення - не найкращий шлях. Погляньте на String.format. Вам потрібно використовувати правильний тип формату, плавати в цьому випадку. Подивіться на мою вище відповідь.
— jjnguy

6
Я проголосував за те, що в мене однакова проблема, і ніхто, здається, тут не розуміє проблеми.
— Обай

1
Як заперечений як десятичний формат, згаданий у дописі Тома, саме те, що ви шукали. Він знімає нулі досить ефективно.
— Стів Померой

4
На вищесказане, може, він хоче обрізати нулі БЕЗ округлення? PS @Pyrolistic, напевно, ви можете просто використовувати number.replaceAll (".? 0 * $", ""); (після містить (".") звичайно)
— Rehno Lindeque

1
Гаразд, то як би ви могли досягти моєї мети за допомогою програми DecimalFormat?
— Піролістичний

8

Використовуйте DecimalFormatіsetMinimumFractionDigits(0)


Я додам setMaximumFractionDigits(2)і setGroupingUsed(false)(ОП не згадує про це, але з прикладу здається, що це потрібно). Також невеликий тестовий випадок не зашкодить з моменту його дрібниці в цьому випадку. І все-таки, оскільки я вважаю, що це найпростіше рішення, ануляція - це нагорода :)
— acrespo

6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}

1
Я думаю, я буду за вами слідувати в цьому: D
— аерокод

5

Зверніть увагу, що String.format(format, args...)це залежить від місця, оскільки він форматується за допомогою локальної мови користувача, тобто, мабуть, з комами та навіть пробілами всередині, наприклад, 123 456,789 або 123,456.789 , , що може бути не зовсім те, що ви очікуєте.

Ви можете віддати перевагу використовувати String.format((Locale)null, format, args...) .

Наприклад,

    double f = 123456.789d;
    System.out.println(String.format(Locale.FRANCE,"%f",f));
    System.out.println(String.format(Locale.GERMANY,"%f",f));
    System.out.println(String.format(Locale.US,"%f",f));

відбитки

123456,789000
123456,789000
123456.789000

і це те, що буде String.format(format, args...) робити в різних країнах.

EDIT Добре, оскільки там обговорювались формальності:

    res += stripFpZeroes(String.format((Locale) null, (nDigits!=0 ? "%."+nDigits+"f" : "%f"), value));
    ...

protected static String stripFpZeroes(String fpnumber) {
    int n = fpnumber.indexOf('.');
    if (n == -1) {
        return fpnumber;
    }
    if (n < 2) {
        n = 2;
    }
    String s = fpnumber;
    while (s.length() > n && s.endsWith("0")) {
        s = s.substring(0, s.length()-1);
    }
    return s;
}

1
слід додати це як коментар до прийнятої відповіді
— Піролістичний

Коментарі не допускають довжини чи формату цього додатку. Оскільки вона додає, можливо, корисну інформацію, я думаю, що її слід дозволити, а не видаляти.
— Terry Jan Reedy,

5

Я зробив DoubleFormatterдля ефективного перетворення великої кількості подвійних значень в хороший / презентабельний рядок:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Якщо ціла частина V має більше MaxInteger => відображення V у вченому форматі (1.2345e + 30), інакше відображається у звичайному форматі 124.45678.
  • MaxDecimal визначає числа десяткових цифр (обрізка з округленням банкіра)

Ось код:

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

/**
 * Convert a double to a beautiful String (US-local):
 * 
 * double horribleNumber = 3598945.141658554548844; 
 * DoubleFormatter df = new DoubleFormatter(4,6);
 * String beautyDisplay = df.format(horribleNumber);
 * String beautyLabel = df.formatHtml(horribleNumber);
 * 
 * Manipulate 3 instances of NumberFormat to efficiently format a great number of double values.
 * (avoid to create an object NumberFormat each call of format()).
 * 
 * 3 instances of NumberFormat will be reused to format a value v:
 * 
 * if v < EXP_DOWN, uses nfBelow
 * if EXP_DOWN <= v <= EXP_UP, uses nfNormal
 * if EXP_UP < v, uses nfAbove
 * 
 * nfBelow, nfNormal and nfAbove will be generated base on the precision_ parameter.
 * 
 * @author: DUONG Phu-Hiep
 */
public class DoubleFormatter
{
    private static final double EXP_DOWN = 1.e-3;
    private double EXP_UP; // always = 10^maxInteger
    private int maxInteger_;
    private int maxFraction_;
    private NumberFormat nfBelow_; 
    private NumberFormat nfNormal_;
    private NumberFormat nfAbove_;

    private enum NumberFormatKind {Below, Normal, Above}

    public DoubleFormatter(int maxInteger, int maxFraction){
        setPrecision(maxInteger, maxFraction);
    }

    public void setPrecision(int maxInteger, int maxFraction){
        Preconditions.checkArgument(maxFraction>=0);
        Preconditions.checkArgument(maxInteger>0 && maxInteger<17);

        if (maxFraction == maxFraction_ && maxInteger_ == maxInteger) {
            return;
        }

        maxFraction_ = maxFraction;
        maxInteger_ = maxInteger;
        EXP_UP =  Math.pow(10, maxInteger);
        nfBelow_ = createNumberFormat(NumberFormatKind.Below);
        nfNormal_ = createNumberFormat(NumberFormatKind.Normal);
        nfAbove_ = createNumberFormat(NumberFormatKind.Above);
    }

    private NumberFormat createNumberFormat(NumberFormatKind kind) {
        final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);
        NumberFormat f = NumberFormat.getInstance(Locale.US);

        //Apply banker's rounding:  this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            //set group separator to space instead of comma

            //dfs.setGroupingSeparator(' ');

            //set Exponent symbol to minus 'e' instead of 'E'
            if (kind == NumberFormatKind.Above) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }

            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (kind == NumberFormatKind.Normal) {
                if (maxFraction_ == 0) {
                    df.applyPattern("#,##0");
                } else {
                    df.applyPattern("#,##0."+sharpByPrecision);
                }
            } else {
                if (maxFraction_ == 0) {
                    df.applyPattern("0E0");
                } else {
                    df.applyPattern("0."+sharpByPrecision+"E0");
                }
            }
        }
        return f;
    } 

    public String format(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        if (v==0) {
            return "0"; 
        }
        final double absv = Math.abs(v);

        if (absv<EXP_DOWN) {
            return nfBelow_.format(v);
        }

        if (absv>EXP_UP) {
            return nfAbove_.format(v);
        }

        return nfNormal_.format(v);
    }

    /**
     * format and higlight the important part (integer part & exponent part) 
     */
    public String formatHtml(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        return htmlize(format(v));
    }

    /**
     * This is the base alogrithm: create a instance of NumberFormat for the value, then format it. It should
     * not be used to format a great numbers of value 
     * 
     * We will never use this methode, it is here only to understanding the Algo principal:
     * 
     * format v to string. precision_ is numbers of digits after decimal. 
     * if EXP_DOWN <= abs(v) <= EXP_UP, display the normal format: 124.45678
     * otherwise display scientist format with: 1.2345e+30 
     * 
     * pre-condition: precision >= 1
     */
    @Deprecated
    public String formatInefficient(double v) {

        final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);

        final double absv = Math.abs(v);

        NumberFormat f = NumberFormat.getInstance(Locale.US);

        //Apply banker's rounding:  this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            //set group separator to space instead of comma

            dfs.setGroupingSeparator(' ');

            //set Exponent symbol to minus 'e' instead of 'E'

            if (absv>EXP_UP) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }
            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (absv<EXP_DOWN || absv>EXP_UP) {
                df.applyPattern("0."+sharpByPrecision+"E0");
            } else {
                df.applyPattern("#,##0."+sharpByPrecision);
            }
        }
        return f.format(v);
    }

    /**
     * Convert "3.1416e+12" to "<b>3</b>.1416e<b>+12</b>"
     * It is a html format of a number which highlight the integer and exponent part
     */
    private static String htmlize(String s) {
        StringBuilder resu = new StringBuilder("<b>");
        int p1 = s.indexOf('.');

        if (p1>0) {
            resu.append(s.substring(0, p1));
            resu.append("</b>");
        } else {
            p1 = 0;
        }

        int p2 = s.lastIndexOf('e');
        if (p2>0) {
            resu.append(s.substring(p1, p2));
            resu.append("<b>");
            resu.append(s.substring(p2, s.length()));
            resu.append("</b>");
        } else {
            resu.append(s.substring(p1, s.length()));
            if (p1==0){
                resu.append("</b>");
            }
        }
        return resu.toString();
    }
}

Примітка: я використав 2 функції з бібліотеки GUAVA. Якщо ви не використовуєте GUAVA, кодуйте його самостійно:

/**
 * Equivalent to Strings.repeat("#", n) of the Guava library: 
 */
private static String createSharp(int n) {
    StringBuilder sb = new StringBuilder(); 
    for (int i=0;i<n;i++) {
        sb.append('#');
    }
    return sb.toString();
}

1
Якщо ви знаєте точність, тоді використовуйте BigDecimal. Дивіться docs.oracle.com/javase/1.5.0/docs/api/java/math/…
— Піролістичний

5

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

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }

5
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 для мінімальної кількості цифр
  2. Відображає # цифр

Хоча це може забезпечити вирішення питання, найкраще додати коротке пояснення для спільноти, щоб отримати користь (і дізнатися) від відповіді
— blurfus

4
String s = String.valueof("your int variable");
while (g.endsWith("0") && g.contains(".")) {
    g = g.substring(0, g.length() - 1);
    if (g.endsWith("."))
    {
        g = g.substring(0, g.length() - 1);
    }
}

замість цього слід просто шукати першу нульову цифру праворуч, а потім скористатись subString (а також переконатися, що рядок містить "." звичайно). таким чином, ви не ввійдете в створення стільки тимчасових рядків на шляху.
— андроїд розробник

3

Пізня відповідь, але ...

Ви сказали, що вирішили зберігати свої номери подвійного типу . Я думаю, що це може бути корінь проблеми, оскільки воно змушує зберігати цілі числа в парні (і тому втрачає початкову інформацію про характер значення). Як щодо зберігання своїх номерів в екземплярах Числа класу (суперклас обох Double і Integer) і покладатися на поліморфізм , щоб визначити правильний формат кожного номера?

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

Приклад:

import java.util.ArrayList;
import java.util.List;

public class UseMixedNumbers {

    public static void main(String[] args) {
        List<Number> listNumbers = new ArrayList<Number>();

        listNumbers.add(232);
        listNumbers.add(0.18);
        listNumbers.add(1237875192);
        listNumbers.add(4.58);
        listNumbers.add(0);
        listNumbers.add(1.2345);

        for (Number number : listNumbers) {
            System.out.println(number);
        }
    }

}

Дасть такий результат:

232
0.18
1237875192
4.58
0
1.2345

javascript зробив той самий вибір до речі :)
— Pyrolistic

@Pyrolistic Ви можете пояснити трохи більше свого твердження? Для мене це не зовсім зрозуміло ... :)
— Намічено

2

Ось що я придумав:

  private static String format(final double dbl) {
    return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
  }

Простий один вкладиш, лише кидає на Int, якщо дійсно потрібно


1
Повторюючи те, що сказав Фелікс Едельманн в іншому місці: це створить незалежну від локалі рядок, що не завжди може бути придатним для користувача.
— JJ Brown

Справедливий момент, для мого використання це не проблема, я зараз не зовсім впевнений, але я думаю, що можна використовувати String.format (з розшукуваним Locale) замість valueOf
— keisar

2

Форматування ціни з групуванням, округленням, без зайвих нулів (подвійно).

Правила:

  1. В кінці немає нулів (2.0000 = 2; 1.0100000 = 1.01 )
  2. Максимум дві цифри після точки (2.010 = 2.01; 0.20 = 0.2 )
  3. Закруглення після 2-ї цифри після точки (1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0 )
  4. Повернення 0(null/-0 = 0 )
  5. Додає $(= $56/-$56 )
  6. Групування ( 101101.02 = $101,101.02)

Більше прикладів:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Написано в Котліні як веселе розширення Double (причина використовується в Android), але її можна легко перетворити на Java, оскільки використовувались класи java.

/**
 * 23.0 -> $23
 *
 * 23.1 -> $23.1
 *
 * 23.01 -> $23.01
 *
 * 23.99 -> $23.99
 *
 * 23.999 -> $24
 *
 * -0.0 -> $0
 *
 * -5.00 -> -$5
 *
 * -5.019 -> -$5.02
 */
fun Double?.formatUserAsSum(): String {
    return when {
        this == null || this == 0.0 -> "$0"
        this % 1 == 0.0 -> DecimalFormat("$#,##0;-$#,##0").format(this)
        else -> DecimalFormat("$#,##0.##;-$#,##0.##").format(this)
    }
}

Як користуватись:

var yourDouble: Double? = -20.00
println(yourDouble.formatUserAsSum()) // will print -$20

yourDouble = null
println(yourDouble.formatUserAsSum()) // will print $0

Про DecimalFormat : https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html


1
public static String fmt(double d) {
    String val = Double.toString(d);
    String[] valArray = val.split("\\.");
    long valLong = 0;
    if(valArray.length == 2){
        valLong = Long.parseLong(valArray[1]);
    }
    if (valLong == 0)
        return String.format("%d", (long) d);
    else
        return String.format("%s", d);
}

Мені довелося скористатися цією причиною, яка d == (long)dпорушила мені порушення в сонарному звіті


1
float price = 4.30;
DecimalFormat format = new DecimalFormat("0.##"); // Choose the number of decimal places to work with in case they are different than zero and zero value will be removed
format.setRoundingMode(RoundingMode.DOWN); // choose your Rounding Mode
System.out.println(format.format(price));

це результат деяких тестів:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4

Що з 1,223450000?
— Alex78191

1.23450000 => 1.23
— Ахмед Міхов

0

Ось два способи її досягнення. По-перше, коротший (і, мабуть, кращий) спосіб:

public static String formatFloatToString(final float f)
  {
  final int i=(int)f;
  if(f==i)
    return Integer.toString(i);
  return Float.toString(f);
  }

А ось довший і, мабуть, гірший шлях:

public static String formatFloatToString(final float f)
  {
  final String s=Float.toString(f);
  int dotPos=-1;
  for(int i=0;i<s.length();++i)
    if(s.charAt(i)=='.')
      {
      dotPos=i;
      break;
      }
  if(dotPos==-1)
    return s;
  int end=dotPos;
  for(int i=dotPos+1;i<s.length();++i)
    {
    final char c=s.charAt(i);
    if(c!='0')
      end=i+1;
    }
  final String result=s.substring(0,end);
  return result;
  }

1
іноді, коли ви робите все простіше, код позаду складніший і менш оптимізований ... але так, ви можете використовувати безліч вбудованих функцій API ...
— android developer

1
Слід почати з простого, і як тільки ви визначите, що у вас є проблеми з роботою, тоді і лише тоді слід оптимізувати. Код для людини читається знову і знову. Змусити його швидко працювати, є другорядним. Якщо ви не використовуєте стандартний API, коли це можливо, ви швидше вводите помилки і лише ускладнюєте їх зміни в майбутньому.
— Піролістичний

3
Я б стверджував, що ви пишете такий код, що НЕ буде швидше. JVM дуже розумний, і ви насправді не знаєте, наскільки швидко чи повільно щось відбувається, доки ви не профілюєте це. Проблеми з продуктивністю можна виявити та виправити, коли це стане проблемою. Не варто передчасно оптимізувати це. Напишіть код для того, щоб люди його читали, а не для того, як ви уявляєте, що машина його запустить. Після того, як це стане проблемою продуктивності, перепишіть код із профілером.
— Піролістичний

2
Хтось ще редагував відповідь, щоб покращити форматування коду. Я розглядав кілька десятків редагувань для затвердження і збирався схвалити їх редагування тут, але зміни були непослідовними, тому я виправив їх. Я також покращив граматику текстових фрагментів.
— Стів Віноскі

1
Я не розумію. Якщо ви сказали, що форматування не має значення, чому ви витратили час, щоб змінити його назад?
— OrhanC1

0

Для Kotlin ви можете використовувати розширення типу:

fun Double.toPrettyString() =
    if(this - this.toLong() == 0.0)
        String.format("%d", this.toLong())
    else
        String.format("%s",this)

0

Ось ще одна відповідь, яка має можливість додавати десятковий ТОЛЬКО, якщо десятковий не був нулем.

   /**
     * Example: (isDecimalRequired = true)
     * d = 12345
     * returns 12,345.00
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * ==================================================
     * Example: (isDecimalRequired = false)
     * d = 12345
     * returns 12,345 (notice that there's no decimal since it's zero)
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * @param d float to format
     * @param zeroCount number decimal places
     * @param isDecimalRequired true if it will put decimal even zero,
     * false will remove the last decimal(s) if zero.
     */
    fun formatDecimal(d: Float? = 0f, zeroCount: Int, isDecimalRequired: Boolean = true): String {
        val zeros = StringBuilder()

        for (i in 0 until zeroCount) {
            zeros.append("0")
        }

        var pattern = "#,##0"

        if (zeros.isNotEmpty()) {
            pattern += ".$zeros"
        }

        val numberFormat = DecimalFormat(pattern)

        var formattedNumber = if (d != null) numberFormat.format(d) else "0"

        if (!isDecimalRequired) {
            for (i in formattedNumber.length downTo formattedNumber.length - zeroCount) {
                val number = formattedNumber[i - 1]

                if (number == '0' || number == '.') {
                    formattedNumber = formattedNumber.substring(0, formattedNumber.length - 1)
                } else {
                    break
                }
            }
        }

        return formattedNumber
    }

-1

Ось відповідь, яка насправді працює (поєднання різних відповідей тут)

public static String removeTrailingZeros(double f)
{
    if(f == (int)f) {
        return String.format("%d", (int)f);
    }
    return String.format("%f", f).replaceAll("0*$", "");
}

1
ви не замінили POINT, наприклад, "100.0" буде перетворено на "100".
— VinceStyling

якщо (f == (int) f) дбає про це.
— Мартін Клосі

2
Не вдалося f = 9999999999.00
— Dawood ibn Kareem

-4

Я знаю, що це справді стара тема. Але я вважаю, що найкращий спосіб зробити це:

public class Test {

    public static void main(String args[]){
        System.out.println(String.format("%s something",new Double(3.456)));
        System.out.println(String.format("%s something",new Double(3.456234523452)));
        System.out.println(String.format("%s something",new Double(3.45)));
        System.out.println(String.format("%s something",new Double(3)));
    }
}

Вихід:

3.456 something
3.456234523452 something
3.45 something
3.0 something

Єдине питання - останній, де .0 не видаляється. Але якщо ти вмієш з цим жити, то це найкраще працює. % .2f округлятиме його до двох останніх десяткових цифр. Так буде і з DecimalFormat. Якщо вам потрібні всі знаки після коми, але не проміжні нулі, то це найкраще працює.


2
DecimalFormat з форматом "#. ##" не буде зберігати додаткові 0, якщо вони не потрібні: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));надрукують1
— Aleks G

ось моя думка. Що робити, якщо ви хочете, щоб 0,0005 відображався, якщо такий є. Ви будете округляти її 2 десятковими цифрами.
— sethu

ОП запитує, як надрукувати цілі значення, що зберігаються в парних знаках :)
— Aleks G

-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Це зробить рядок для скидання хвостових 0-х.


1
Це хороше вирішення питання, якби їх цікавило лише те, що кінцеві нулі скидаються, як би ви змінили свій код, щоб також обрізати крапку після коми? тобто "1."
— bakoyaro

29
Будьте уважні, ваше рішення перетворить 1000 в 1, що неправильно.
— Алекс Г
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.