Comment bien formater les nombres flottants en chaîne sans décimal inutile 0?
Un double 64 bits peut représenter un entier +/- 253 exactement
Compte tenu de ce fait, je choisis d'utiliser un type double comme un type unique pour tous mes types, puisque mon plus grand entier est non 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 je reçois beaucoup de zéros pour les petits valeur.
Voici un exemple de sortie de 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 c'est beaucoup de perte de performance due à la manipulation de chaînes. Puis-je faire mieux avec un autre code de format?
Modifier
Les réponses de Tom E. et Jeremy S. sont inacceptables car ils arrondir arbitrairement à 2 décimales. Veuillez comprendre le problème avant de répondre.
Modifier 2
Veuillez noter que String.format(format, args...)
est dépendant de paramètres régionaux (voir les réponses ci-dessous).
22 réponses
Si l'idée est d'imprimer des entiers stockés sous forme de doubles 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.
new DecimalFormat("#.##").format(1.199); //"1.2"
Comme indiqué dans les commentaires, ce n'est pas la bonne réponse à la question d'origine.
Cela dit, c'est un moyen très utile de formater des nombres sans zéros de fin inutiles.
En bref:
Si vous voulez 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 d'autres réponses ne me convenaient pas:
-
Double.toString()
ouSystem.out.println
ouFloatingDecimal.toJavaFormatString
utilise des notations scientifiques si un double est inférieur à 10^-3 ou supérieure ou égale à 10^7double 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 un supplément zéros ajoutés si vous avez moins de décimales. Exemple:double myValue = 0.00000021d; String.format("%.12f", myvalue); //output: 0.000000210000
-
En utilisant
setMaximumFractionDigits(0);
ou%.0f
vous supprimez toute précision décimale, ce qui est bien pour les entiers / longs mais pas pour doubledouble 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. En 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 vous permet d'obtenir un point pour le séparateur décimal, partout où votre programme s'exécutera
Pourquoi en utilisant 340 alors pour setMaximumFractionDigits
?
Deux raisons :
-
setMaximumFractionDigits
accepte un entier mais son implémentation a un nombre maximum autorisé de {[13] } qui 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
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 supportées par Double. Rendements:
0.12
12
12.144252
0
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);
}
Mes 2 cents:
if(n % 1 == 0) {
return String.format(Locale.US, "%.0f", n));
} else {
return String.format(Locale.US, "%.1f", n));
}
Non, peu importe.
La perte de Performance due à la manipulation de chaînes est nulle.
Et voici le code pour couper la fin d'après %f
private static String trimTrailingZeros(String number) {
if(!number.contains(".")) {
return number;
}
return number.replaceAll("\\.?0*$", "");
}
Veuillez noter que String.format(format, args...)
est dépendant des paramètres régionaux parce qu'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 n'est peut-être pas exactement ce que vous attendez.
Vous pouvez 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));
Imprime
123456,789000
123456,789000
123456.789000
Et c'est ce que String.format(format, args...)
fera dans différents pays.
Modifier Ok, car il y a eu un 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;
}
J'ai fait un DoubleFormatter
pour 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.2345 e + 30) sinon, afficher au format normal 124.45678.
- le MaxDecimal décide des nombres de chiffres décimaux (trim avec l'arrondi du banquier)
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();
}
}
Note: j'ai utilisé 2 fonctions de la bibliothèque goyave. Si vous n'utilisez pas de goyave, coder 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();
}
if (d == Math.floor(d)) {
return String.format("%.0f", d);
} else {
return Double.toString(d);
}
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);
}
}
Celui - ci fera bien le travail, je sais que le sujet est vieux, mais je luttais avec le même problème jusqu'à ce que j'arrive à cela. J'espère que quelqu'un la trouver utile.
public static String removeZero(double number) {
DecimalFormat format = new DecimalFormat("#.###########");
return format.format(number);
}
new DecimalFormat("00.#").format(20.236)
//out =20.2
new DecimalFormat("00.#").format(2.236)
//out =02.2
- 0 pour le nombre minimum de chiffres
- Rend # chiffres
Réponse Tardive mais...
Vous avez dit que vous Choisissez pour stocker vos numéros avec le Double type . Je pense que cela pourrait être la racine du problème car cela vous oblige à stocker entiers en doubles (et donc perdre les informations initiales sur la nature de la valeur). Qu'en est-il de stocker vos nombres dans les instances de la classe Number (superclasse de Double et D'entier) et de compter sur le polymorphisme pour déterminer le format correct de chaque nombre ?
Je sais qu'il peut ne pas être acceptable de refactoriser une partie entière de votre code à cause de cela, mais cela pourrait produire la sortie désiré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
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;
}
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);
}
J'ai dû utiliser cette cause {[1] } me donnait une violation dans le rapport sonar
C'est ce que j'ai trouvé:
private static String format(final double dbl) {
return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
}
Simple une doublure, seulement jette à int si vraiment besoin de
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*$", "");
}
Je sais que c'est un très vieux fil.. Mais je pense que la meilleure façon de le faire est comme ci-dessous:
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)));
}
}
Sortie:
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 capable de vivre avec cela, cela fonctionne mieux. %.2f arrondira aux 2 derniers chiffres décimaux. Ainsi sera DecimalFormat. Si vous avez besoin de toutes les décimales mais pas des zéros de fin, cela fonctionne mieux.
String s = "1.210000";
while (s.endsWith("0")){
s = (s.substring(0, s.length() - 1));
}
Cela rendra la chaîne pour laisser tomber les 0-s.