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

Конечно, я могу написать функцию для обрезки этих нулей, но это большая потеря производительности из-за манипуляций со строками. Могу ли я сделать лучше с другим форматом кода?

РЕДАКТИРОВАТЬ

Ответы Тома Э. и Джереми С. неприемлемы, поскольку они оба произвольно округляют до 2 десятичных знаков. Пожалуйста, поймите проблему, прежде чем ответить.

РЕДАКТИРОВАТЬ 2

Обратите внимание, что String.format(format, args...)это зависит от локали (см. Ответы ниже).

Pyrolistical
источник
Если все, что вы хотите, это целые числа, почему бы не использовать длинное? Вы получаете больше ударов при 2 ^ 63-1, без неудобного форматирования и лучшей производительности.
Бассеро
14
Потому что некоторые значения на самом деле удваиваются
Пиролистический
2
В некоторых случаях, когда
возникала
Это только у меня или 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

И не полагается на манипуляции со строками.

JasonD
источник
9
Согласитесь, это плохой ответ, не используйте его. Он не работает с doubleбольшим, чем максимальное intзначение. Даже с longэтим все равно не получится для огромного количества. Кроме того, он вернет String в экспоненциальной форме, например, «1.0E10», для больших значений, что, вероятно, не то, что хочет запрашивающий. Используйте %fвместо %sвторой строки формата, чтобы исправить это.
JLH
26
ОП явно заявил, что они не хотят, чтобы вывод форматировался с использованием %f. Ответ зависит от описанной ситуации и желаемого результата. ОП предположил, что их максимальным значением было 32-битное целое число без знака, которое, как я понял, intбыло приемлемым (без знака на самом деле не существует в Java, и ни один пример не был проблематичным), но его изменение intна longтривиальное исправление, если ситуация отличается.
JasonD
1
Где в вопросе сказано, что не следует этого делать?
JasonD
6
String.format("%s",d)??? Поговорим о ненужных накладных расходах. Использование Double.toString(d). То же самое для другого: Long.toString((long)d).
Андреас
15
Проблема в том, что %sне работает с локалями. В немецком языке мы используем «,» вместо «.» в десятичных числах. В то время как String.format(Locale.GERMAN, "%f", 1.5)возвращает «1,500000», 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»). Format (amount);) this не то, что хотел ОП, но может быть полезно для справки.
Ти Джей Эллис
22
Да, как первоначальный автор вопроса, это неправильный ответ. Забавно, сколько голосов за. Проблема с этим решением заключается в том, что оно произвольно округляется до 2 десятичных знаков.
пиролистический
11
@Mazyod, потому что вы всегда можете передать плавающее указание с большим количеством десятичных знаков, чем формат. Это написание кода, который будет работать большую часть времени, но не охватывать все крайние случаи.
пиролистический
15
@Pyrolistical - ИМХО, есть много отрицательных отзывов, потому что, хотя это неправильное решение для вас, это правильное решение для 99% + тех, кто находит этот вопрос и ответ: обычно последние несколько цифр двойного числа являются «шумом», которые загромождают вывод, мешая удобочитаемости. Следовательно, программист определяет, сколько цифр выгодно человеку, читающему вывод, и указывает, сколько из них. Распространенной ситуацией является небольшое накопление математических ошибок, поэтому значение может быть 12,000000034, но предпочтительнее округлить до 12 и компактно отобразить как «12». И "12.340000056" => "12.34".
ToolmakerSteve
226
String.format("%.2f", value) ;
Джереми Слэйд
источник
13
Это правильно, но всегда печатает конечные нули, даже если нет дробной части. String.format ("%. 2f, 1.0005) печатает 1,00, а не 1. Существует ли какой-либо спецификатор формата, чтобы не печатать дробную часть, если она не существует?
Эмре Язичи
87
Проголосовали за отрицание, поскольку вопрос состоит в том, чтобы убрать все конечные нули, и этот ответ всегда будет оставлять две плавающие точки независимо от того, равен ли он нулю.
Сулаксия
Десятичный формат был хорошим трюком - хотя я и использовал его для своей ситуации (таймер уровня игры), так как конечные нули выглядели лучше.
Тимоти Ли Рассел
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использует научные записи, если double меньше 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 цифрами вы точно не округлите свою двойную и потерянную точность
JBE
источник
Это не работает для целых чисел, например, «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
22

Мои 2 цента:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}
Фернандо Гальего
источник
2
Или просто return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Emperor
сбой, когда 23.00123 ==> 23.00
aswzen
11

Нет, неважно.

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

И вот код, чтобы обрезать конец после %f

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

    return number.replaceAll("\\.?0*$", "");
}
Pyrolistical
источник
7
Я понизил голосование, потому что ваше решение не лучший путь. Посмотрите на String.format. Вам нужно использовать правильный тип формата, плавающий в этом случае. Посмотрите на мой ответ выше.
02:28
6
Я проголосовал, потому что у меня та же проблема, и никто здесь, кажется, не понимает проблему.
Обей
1
Упущенное в качестве десятичного формата, упомянутого в сообщении Тома, именно то, что вы искали. Он эффективно удаляет нули.
Стив Померой
4
К вышесказанному может быть он хочет обрезать нули БЕЗ округления? PS @Pyrolistics, конечно, вы можете просто использовать number.replaceAll (".? 0 * $", ""); (после содержит (".") конечно)
Рено Линдек
1
Хорошо, тогда как вы сможете достичь моей цели с помощью DecimalFormat?
Пиролистический
8

Используйте DecimalFormatиsetMinimumFractionDigits(0)

vlazzle
источник
Я бы добавил setMaximumFractionDigits(2)и setGroupingUsed(false)(OP не упоминает об этом, но из примера кажется, что это требуется). Кроме того, небольшой тестовый пример не повредит, поскольку в этом случае он тривиален. Тем не менее, поскольку я думаю, что это самое простое решение, upvote - это upvote :)
acrespo
6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}
fop6316
источник
1
Я думаю, что буду следовать за вами в этом: D
aeracode
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...) делать в разных странах.

РЕДАКТИРОВАТЬ ОК, так как было обсуждение формальностей:

    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;
}
18446744073709551615
источник
1
Вы должны добавить это как комментарий к принятому ответу
Пиролистический
Комментарии не допускают длину или формат этого приложения. Поскольку он добавляет, возможно, полезную информацию, я думаю, что она должна быть разрешена как есть, а не удалена.
Терри Ян Риди
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();
}
Hiep
источник
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);
    }
Bialy
источник
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);
    }
}
Камаль
источник
вместо этого вы должны просто найти первую ненулевую цифру справа и затем использовать подстроку (а также убедиться, что строка содержит «.», конечно). Таким образом, вы не будете создавать столько временных строк на этом пути.
Android-разработчик
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 Можете ли вы объяснить немного больше вашего утверждения? Это не совсем понятно для меня ... :)
Пятнистый
2

Вот что я придумал:

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

Простой один лайнер, только бросает к int, если действительно нужно

keisar
источник
1
Повторение того, что Феликс Эдельманн сказал в другом месте: это создаст независимую от локали строку, которая не всегда может быть подходящей для пользователя.
Джей Джей Браун
Честно говоря, для моего случая использования это не было проблемой, я не совсем уверен прямо сейчас, но я думаю, что можно использовать 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

Написанный на Kotlin как забавное расширение 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

О десятичном формате : https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html.

oxied
источник
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дала мне нарушение в отчете сонара

ShAkKiR
источник
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.23450000?
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;
  }
разработчик Android
источник
1
иногда, когда вы делаете вещи проще, код позади становится более сложным и менее оптимизированным ... но да, вы можете использовать множество встроенных функций API ...
Android-разработчик
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)
Levor
источник
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
    }
Tenten Ponce
источник
-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
вы не заменили ТОЧКУ, например, «100.0» будет преобразовано в «100».
VinceStyling
если (f == (int) f) позаботится об этом.
Мартин Клоси
2
Сбой f = 9999999999.00
Дауд ибн Карим
-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 округляет его до 2 последних десятичных цифр. Так будет и с DecimalFormat. Если вам нужны все десятичные разряды, но не конечные нули, то это работает лучше всего.

Sethu
источник
2
DecimalFormat с форматом "#. ##" не будет хранить лишние 0, если они не нужны: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));будет напечатано1
Aleks G
это моя точка зрения. Что делать, если вы хотите, чтобы 0,0005 отображались, если они есть. Вы будете округлять его до 2 десятичных цифр.
Сетху
ОП спрашивает, как вывести целочисленные значения, хранящиеся в двойных
числах
-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Это заставит строку отбросить хвостовые 0-s.

Кади
источник
1
Это хорошее решение вопроса, если бы они были заинтересованы только в отбрасывании конечных нулей, как бы вы изменили свой код, чтобы также обрезать конечную десятичную точку? то есть "1."
Bakoyaro
29
Будьте осторожны, ваше решение преобразует 1000 в 1, что неправильно.
Алекс Г