Comment bien formater les nombres flottants en chaîne sans décimal inutile 0?

496

Un double 64 bits peut représenter un entier +/- 2 53 exactement

Compte tenu de ce fait, je choisis d'utiliser un type double comme type unique pour tous mes types, car mon plus grand entier n'est pas signé 32 bits.

Mais maintenant, je dois imprimer ces pseudo entiers, mais le problème est qu'ils sont également mélangés avec des doubles réels.

Alors, comment puis-je imprimer ces doubles bien en Java?

J'ai essayé String.format("%f", value), ce qui est proche, sauf que j'obtiens beaucoup de zéros de fin pour les petites valeurs.

Voici un exemple de sortie de %f

232.00000000
0,18000000000
1237875192.0
4.5800000000
0.00000000
1.23450000

Ce que je veux c'est:

232
0,18
1237875192
4,58
0
1,2345

Bien sûr, je peux écrire une fonction pour couper ces zéros, mais cela représente beaucoup de perte de performances en raison de la manipulation de String. Puis-je faire mieux avec un autre code de format?

ÉDITER

Les réponses de Tom E. et Jeremy S. sont inacceptables car elles arrondissent toutes les deux arbitrairement à 2 décimales. Veuillez comprendre le problème avant de répondre.

EDIT 2

Veuillez noter que cela String.format(format, args...)dépend des paramètres régionaux (voir les réponses ci-dessous).

Pyrolistique
la source
Si vous ne voulez que des entiers, pourquoi ne pas utiliser un long? Vous obtenez plus de coup à 2 ^ 63-1, pas de formatage maladroit et de meilleures performances.
basszero
14
Parce que certaines valeurs sont en fait doubles
Pyrolistical
2
Certains cas où ce problème est survenu ont été un bug corrigé dans JDK 7: stackoverflow.com/questions/7564525/…
Pyrolistical
Est-ce juste moi ou JavaScript est-il 100% meilleur en conversion de nombre en chaîne que Java?
Andy
System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
Shayan Amani

Réponses:

399

Si l'idée est d'imprimer des entiers stockés en double comme s'ils étaient des entiers, et sinon d'imprimer les doubles avec la précision minimale nécessaire:

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

Produit:

232
0.18
1237875192
4.58
0
1.2345

Et ne repose pas sur la manipulation de chaînes.

JasonD
la source
9
D'accord, c'est une mauvaise réponse, ne l'utilisez pas. Il ne fonctionne pas avec une valeur doublesupérieure à la intvaleur maximale . Même avec longcela, il échouerait toujours pour un grand nombre. De plus, il retournera une chaîne sous une forme exponentielle, par exemple "1.0E10", pour les grandes valeurs, ce qui n'est probablement pas ce que le demandeur veut. Utilisez %fau lieu de %sdans la deuxième chaîne de format pour résoudre ce problème.
jlh
26
L'OP a déclaré explicitement qu'il ne voulait pas que la sortie soit formatée en utilisant %f. La réponse est spécifique à la situation décrite et au résultat souhaité. L'OP a suggéré que leur valeur maximale était un entier non signé 32 bits, ce que j'ai compris intcomme acceptable (non signé qui n'existait pas réellement en Java, et aucun exemple n'était problématique), mais le passage intà longest un correctif trivial si la situation est différente.
JasonD
1
Où dans la question dit-il qu'il ne devrait pas faire cela?
JasonD
6
String.format("%s",d)??? Parlez des frais généraux inutiles. Utilisez Double.toString(d). Idem pour l'autre: Long.toString((long)d).
Andreas
15
Le problème est que %scela ne fonctionne pas avec les paramètres régionaux. En allemand, nous utilisons un "," au lieu d'un "." en nombres décimaux. Alors que String.format(Locale.GERMAN, "%f", 1.5)renvoie "1 500 000", String.format(Locale.GERMAN, "%s", 1.5)renvoie "1,5" - avec un ".", Ce qui est faux en langue allemande. Existe-t-il également une version dépendante des paramètres régionaux de «% s»?
Felix Edelmann
414
new DecimalFormat("#.##").format(1.199); //"1.2"

Comme indiqué dans les commentaires, ce n'est pas la bonne réponse à la question initiale.
Cela dit, c'est un moyen très utile de formater les nombres sans zéros de fin inutiles.

Tom Esterez
la source
16
Une note importante ici est que 1.1 serait correctement formaté en tant que "1.1" sans zéros de fin.
Steve Pomeroy
53
Et s'il vous arrive de vouloir un nombre spécifique de zéros de fin (par exemple, si vous imprimez des montants en argent), vous pouvez utiliser '0' au lieu de '#' (c'est-à-dire le nouveau format décimal ("0.00"). Format (montant);) ce n'est pas ce que OP voulait, mais peut être utile comme référence.
TJ Ellis,
22
Oui, en tant qu'auteur original de la question, c'est la mauvaise réponse. C'est drôle le nombre de votes positifs. Le problème avec cette solution est qu'elle arrondit arbitrairement à 2 décimales.
pyrolyse
11
@Mazyod car vous pouvez toujours passer dans un pointage flottant avec plus de décimales que le format. C'est de l'écriture de code qui fonctionnera la plupart du temps mais ne couvrira pas tous les cas marginaux.
Pyrolistical
15
@Pyrolistical - IMHO, il y a beaucoup de votes positifs parce que, bien que ce ne soit pas la bonne solution pour vous, c'est la bonne solution pour 99% + de ceux qui trouvent ce Q&R: généralement, les derniers chiffres d'un double sont du "bruit", qui encombrent la sortie, gênant la lisibilité. Par conséquent, le programmeur détermine combien de chiffres sont bénéfiques pour la personne qui lit la sortie et spécifie ce nombre. Une situation courante est que de petites erreurs mathématiques se sont accumulées, donc une valeur peut être 12,000000034, mais préfère arrondir à 12 et s'afficher de manière compacte sous la forme "12". Et "12.340000056" => "12,34".
ToolmakerSteve
226
String.format("%.2f", value) ;
Jeremy Slade
la source
13
C'est correct, mais imprime toujours des zéros de fin même s'il n'y a pas de partie fractionnaire. String.format ("%. 2f, 1.0005) imprime 1,00 et non 1. Existe-t-il un spécificateur de format pour ne pas imprimer la partie fractionnaire si elle n'existe pas?
Emre Yazici
87
Down a voté car la question demande de supprimer tous les zéros à la fin et cette réponse laissera toujours deux virgules flottantes, même si elle est nulle.
Zulaxia
Le DecimalFormat était une bonne astuce - même si j'ai fini par utiliser celui-ci pour ma situation (minuterie de niveau de jeu) car les zéros à la fin semblaient meilleurs.
Timothy Lee Russell
2
Je pense que vous pouvez gérer correctement les zéros de fin en utilisant g au lieu de f.
Peter Ajtai
3
J'ai utilisé cette solution dans un système de production avec "% .5f", et elle est vraiment très mauvaise, ne l'utilisez pas ... car elle a imprimé ceci: 5.12E-4 au lieu de 0.000512
hamish
87

En bref:

Si vous souhaitez vous débarrasser des zéros de fin et des problèmes de paramètres régionaux, vous devez utiliser:

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

Explication:

Pourquoi les autres réponses ne me convenaient pas:

  • Double.toString()ou System.out.printlnou FloatingDecimal.toJavaFormatStringutilise des notations scientifiques si le double est inférieur à 10 ^ -3 ou supérieur ou égal à 10 ^ 7

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • en utilisant %f, la précision décimale par défaut est 6, sinon vous pouvez la coder en dur mais cela entraîne l'ajout de zéros supplémentaires si vous avez moins de décimales. Exemple :

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • en utilisant setMaximumFractionDigits(0);ou %.0fvous supprimez toute précision décimale, ce qui est bien pour les entiers / longs mais pas pour le double

    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
  • en utilisant DecimalFormat, vous êtes dépendant local. Dans les paramètres régionaux français, le séparateur décimal est une virgule, pas un point:

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

    L'utilisation des paramètres régionaux ANGLAIS garantit que vous obtenez un point pour le séparateur décimal, où que votre programme s'exécute

Pourquoi utiliser 340 alors setMaximumFractionDigits?

Deux raisons :

  • setMaximumFractionDigitsaccepte un entier mais son implémentation a un nombre maximum autorisé de chiffres DecimalFormat.DOUBLE_FRACTION_DIGITSqui est égal à 340
  • Double.MIN_VALUE = 4.9E-324 donc avec 340 chiffres vous êtes sûr de ne pas arrondir votre précision double et lâche
JBE
la source
Cela ne fonctionne pas pour les nombres entiers, par exemple "2" devient "2".
kap
Merci, j'ai corrigé la réponse en utilisant le modèle 0au lieu de#.
JBE
Vous n'utilisez pas la constante DecimalFormat.DOUBLE_FRACTION_DIGITSmais vous utilisez la valeur 340, pour laquelle vous fournissez ensuite un commentaire pour montrer qu'elle est égale DecimalFormat.DOUBLE_FRACTION_DIGITS. Pourquoi ne pas simplement utiliser la constante ???
Maarten Bodewes
1
Parce que cet attribut n'est pas public ... il est "compatible avec les packages"
JBE
4
Merci! En fait, cette réponse est la seule qui correspond vraiment à toutes les exigences mentionnées dans la question - elle n'affiche pas de zéros inutiles, ne arrondit pas les chiffres et dépend des paramètres régionaux. Génial!
Felix Edelmann
26

Pourquoi pas:

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

Cela devrait fonctionner avec les valeurs extrêmes prises en charge par Double. Rendements:

0.12
12
12.144252
0
Valeriu Paloş
la source
2
Je préfère cette réponse par laquelle nous n'avons pas besoin de faire de conversion de type.
Jeff T.
Brève explication: "%s"appelle essentiellement d.toString()mais cela ne fonctionne pas avec intou si d==null!
Néph
24

Sur ma machine, la fonction suivante est environ 7 fois plus rapide que la fonction fournie par la réponse de JasonD , car elle évite String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}
Rok Strniša
la source
1
Hmm, cela ne prend pas en compte les paramètres régionaux, mais JasonD non plus.
TWiStErRob
22

Mes 2 cents:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}
Fernando Gallego
la source
2
Ou tout simplement return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Emperor
échec lorsque 23.00123 ==> 23.00
aswzen
11

Naw, tant pis.

La perte de performances due à la manipulation de chaînes est nulle.

Et voici le code pour couper la fin après %f

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

    return number.replaceAll("\\.?0*$", "");
}
Pyrolistique
la source
7
J'ai voté contre parce que votre solution n'est pas la meilleure solution. Jetez un œil à String.format. Vous devez utiliser le type de format correct, float dans ce cas. Regardez ma réponse ci-dessus.
jjnguy
6
J'ai voté parce que j'ai le même problème, et personne ici ne semble comprendre le problème.
Obay
1
Downvoted comme DecimalFormat mentionné dans le post de Tom est exactement ce que vous cherchiez. Il supprime les zéros assez efficacement.
Steve Pomeroy
4
Pour ce qui précède, peut-être qu'il veut couper les zéros sans arrondir? PS @Pyrolistical, vous pouvez certainement utiliser number.replaceAll (".? 0 * $", ""); (après contient (".") bien sûr)
Rehno Lindeque
1
Ok, alors comment pourriez-vous atteindre mon objectif avec le DecimalFormat?
Pyrolistical
8

Utilisez un DecimalFormatetsetMinimumFractionDigits(0)

vlazzle
la source
J'ajouterais setMaximumFractionDigits(2)et setGroupingUsed(false)(OP ne le mentionne pas mais à partir de l'exemple il semble que son requis). De plus, un petit cas de test ne fait pas de mal depuis son trivial dans ce cas. Pourtant, puisque je pense que c'est la solution la plus simple, un vote positif est un vote positif :)
acrespo
6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}
fop6316
la source
1
Je pense que je vais vous suivre dans ceci: D
aeracode
5

Veuillez noter que cela String.format(format, args...)dépend des paramètres régionaux car il formate en utilisant les paramètres régionaux par défaut de l'utilisateur, c'est-à-dire, probablement avec des virgules et même des espaces à l'intérieur comme 123 456 789 ou 123 456,789 , ce qui peut ne pas être exactement ce que vous attendez.

Vous préférerez peut-être utiliser String.format((Locale)null, format, args...) .

Par exemple,

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

impressions

123456,789000
123456,789000
123456.789000

et c'est ce qui va String.format(format, args...) fera dans différents pays.

EDIT Ok, puisqu'il y a eu une discussion sur les formalités:

    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
la source
1
vous devez ajouter ceci en tant que commentaire à la réponse acceptée
Pyrolistical
Les commentaires ne permettent pas la longueur ou le format de cet addendum. Puisqu'il ajoute des informations éventuellement utiles, je pense qu'il devrait être autorisé tel quel plutôt que supprimé.
Terry Jan Reedy
5

J'ai fait un DoubleFormatterpour convertir efficacement un grand nombre de valeurs doubles en une chaîne agréable / présentable:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Si la partie entière de V a plus de MaxInteger => afficher V au format scientifique (1.2345e + 30) sinon afficher au format normal 124.45678.
  • le MaxDecimal décide des nombres de chiffres décimaux (rognage avec arrondi bancaire)

Voici le code:

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

Remarque: j'ai utilisé 2 fonctions de la bibliothèque GUAVA. Si vous n'utilisez pas GUAVA, codez-le vous-même:

/**
 * 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
la source
1
Si vous connaissez la précision, utilisez un BigDecimal. Voir docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pyrolistical
5

Celui-ci fera bien le travail, je sais que le sujet est ancien, mais je luttais avec le même problème jusqu'à ce que j'y arrive. J'espère que quelqu'un le trouvera utile.

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }
Bialy
la source
5
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 pour le nombre minimum de chiffres
  2. Rendu # chiffres
Hossein Hajizadeh
la source
bien que cela puisse apporter une solution à la question, il est
recommandé
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);
    }
}
kamal
la source
à la place, vous devez simplement rechercher le premier chiffre non nul à partir de la droite, puis utiliser la sous-chaîne (et également vérifier que la chaîne contient bien "."). De cette façon, vous ne créerez pas autant de chaînes temporaires en cours de route.
développeur Android
3

Réponse tardive mais ...

Vous avez dit que vous choisissez de stocker vos numéros avec le double type . Je pense que cela pourrait être la racine du problème car cela vous oblige à stocker des entiers en doubles (et donc à perdre les informations initiales sur la nature de la valeur). Qu'en est-il du stockage de vos numéros dans les instances du numéro classe (superclasse de Double et Integer) et comptez sur le polymorphisme pour déterminer le format correct de chaque nombre?

Je sais qu'il n'est peut-être pas acceptable de refactoriser une partie entière de votre code à cause de cela, mais cela pourrait produire la sortie souhaitée sans code / casting / analyse supplémentaire.

Exemple:

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

}

Produira la sortie suivante:

232
0.18
1237875192
4.58
0
1.2345
Pointé
la source
javascript a d'ailleurs fait le même choix :)
Pyrolistical
@Pyrolistique Pouvez-vous expliquer un peu plus votre déclaration? Ce n'est pas tout à fait clair pour moi ... :)
Repéré
2

Voici ce que j'ai trouvé:

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

Une doublure simple, ne se jette que si vous en avez vraiment besoin

keisar
la source
1
Répéter ce que Felix Edelmann a dit ailleurs: cela créera une chaîne indépendante des paramètres régionaux, qui peut ne pas toujours être appropriée pour l'utilisateur.
JJ Brown
juste point, pour mon cas d'utilisation, ce n'était pas un problème, je ne suis pas tout à fait sûr pour le moment mais je pense que l'on pourrait utiliser String.format (avec les paramètres régionaux recherchés) au lieu de valueOf
keisar
2

Format de prix avec regroupement, arrondi, pas de zéros inutiles (en double).

Règles:

  1. Pas de zéros à la fin (2.0000 = 2; 1.0100000 = 1.01 )
  2. Deux chiffres maximum après un point (2.010 = 2.01; 0.20 = 0.2 )
  3. Arrondi après le 2ème chiffre après un point (1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0 )
  4. Retours 0(null/-0 = 0 )
  5. Ajoute $(= $56/-$56 )
  6. Regroupement ( 101101.02 = $101,101.02)

Plus d'exemples:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Écrit dans Kotlin comme extension amusante de Double (cause utilisée dans Android), mais peut être converti facilement en Java, car des classes java ont été utilisées.

/**
 * 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)
    }
}

Comment utiliser:

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

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

À propos de DecimalFormat : https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

oxied
la source
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);
}

Je devais utiliser cette cause d == (long)dme donnait une violation dans le rapport du sonar

ShAkKiR
la source
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));

c'est le résultat de quelques tests:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4
Ahmed Mihoub
la source
Qu'en est-il de 1.23450000?
Alex78191
1.23450000 => 1.23
Ahmed Mihoub
0

Voici deux façons d'y parvenir. Tout d'abord, la manière la plus courte (et probablement la meilleure):

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

Et voici la façon la plus longue et probablement la pire:

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;
  }
développeur android
la source
1
parfois, lorsque vous simplifiez les choses, le code derrière est plus complexe et moins optimisé ... mais oui, vous pouvez utiliser de nombreuses fonctions API intégrées ...
développeur Android
1
Vous devez commencer par simple et une fois que vous avez déterminé que vous avez un problème de performances, vous devez alors et uniquement optimiser. Le code est pour l'homme de lire encore et encore. Le faire fonctionner rapidement est secondaire. En n'utilisant pas l'API standard dans la mesure du possible, vous êtes plus susceptible d'introduire des bogues et cela ne fera que le rendre plus difficile à changer à l'avenir.
Pyrolistical
3
Je dirais que le code que vous écrivez comme ça ne sera pas plus rapide. La JVM est très intelligente et vous ne savez pas à quel point quelque chose est rapide ou lent jusqu'à ce que vous le profiliez. Les problèmes de performances peuvent être détectés et résolus quand cela devient un problème. Vous ne devez pas l'optimiser prématurément pour cela. Écrivez du code pour que les gens le lisent, pas pour la façon dont vous imaginez que la machine va l'exécuter. Une fois que cela devient un problème de performances, réécrivez le code avec un profileur.
Pyrolistical
2
Quelqu'un d'autre a modifié la réponse pour améliorer la mise en forme du code. J'examinais plusieurs dizaines de modifications pour approbation et j'allais approuver leur modification ici, mais les modifications étaient incohérentes, je les ai donc corrigées. J'ai également amélioré la grammaire des extraits de texte.
Steve Vinoski
1
Je ne comprends pas. Si vous avez dit que la mise en forme n'avait pas d'importance, pourquoi avez-vous pris le temps de la modifier à nouveau?
OrhanC1
0

Pour Kotlin, vous pouvez utiliser une extension comme:

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

Voici une autre réponse qui a une option pour ajouter la décimale UNIQUEMENT SI la décimale n'était pas nulle.

   /**
     * 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
la source
-1

Voici une réponse qui fonctionne réellement (combinaison de différentes réponses ici)

public static String removeTrailingZeros(double f)
{
    if(f == (int)f) {
        return String.format("%d", (int)f);
    }
    return String.format("%f", f).replaceAll("0*$", "");
}
Martin Klosi
la source
1
vous n'avez pas remplacé le POINT, par exemple, "100.0" sera converti en "100".
VinceStyling
si (f == (int) f) s'en charge.
Martin Klosi
2
Échoue sur f = 9999999999,00
Dawood ibn Kareem
-4

Je sais que c'est un fil très ancien .. Mais je pense que la meilleure façon de le faire est la suivante:

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

Production:

3.456 something
3.456234523452 something
3.45 something
3.0 something

Le seul problème est le dernier où .0 n'est pas supprimé. Mais si vous êtes en mesure de vivre avec cela, cela fonctionne mieux. % .2f l'arrondira aux 2 derniers chiffres décimaux. DecimalFormat aussi. Si vous avez besoin de toutes les décimales mais pas des zéros de fin, cela fonctionne mieux.

sethu
la source
2
DecimalFormat avec le format "#. ##" ne gardera pas 0 supplémentaire s'il n'est pas nécessaire: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));imprimera1
Aleks G
c'est mon point. Que faire si vous voulez afficher le 0,0005 s'il y en a. Vous l'arrondirez sur 2 chiffres décimaux.
sethu
L'OP demande comment imprimer les valeurs entières stockées en double :)
Aleks G
-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Cela fera que la chaîne laissera tomber les 0-s de queue.

Kadi
la source
1
C'est une bonne solution à la question, s'ils étaient seulement intéressés par la suppression des zéros de fin, comment changeriez-vous votre code pour également couper un point décimal de fin? c'est-à-dire "1."
bakoyaro
29
Attention, votre solution convertira 1000 en 1, ce qui est faux.
Aleks G