Як добре відформатувати плаваючі числа до 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.