¿Cómo formatear muy bien los números flotantes en String sin un decimal 0 innecesario?

Un doble de 64 bits puede representar un entero +/- 2 53 exactamente

Dado este hecho, elijo usar un tipo doble como un solo tipo para todos mis tipos, ya que mi entero más grande no tiene signo de 32 bits.

Pero ahora tengo que imprimir estos pseudo-enteros, pero el problema es que también están mezclados con dobles reales.

Entonces, ¿cómo imprimo estos dobles muy bien en Java?

He intentado String.format("%f", value) , que está cerca, excepto que recibo muchos ceros finales para valores pequeños.

Aquí hay un ejemplo de salida de %f

 232.00000000
 0.18000000000
 1237875192.0
 4.5800000000
 0.00000000
 1.23450000

Lo que quiero es:

 232
 0.18
 1237875192
 4.58
 0
 1.2345

Claro que puedo escribir una función para recortar esos ceros, pero esa es una gran pérdida de rendimiento debido a la manipulación de cadenas. ¿Puedo mejorar con otro código de formato?

EDITAR

Las respuestas de Tom E. y Jeremy S. son inaceptables ya que ambas se redondean arbitrariamente a 2 decimales. Por favor, comprenda el problema antes de responder.

EDIT 2

Tenga en cuenta que String.format(format, args...) depende de la configuración regional (vea las respuestas a continuación).

Si la idea es imprimir enteros almacenados como dobles como si fueran enteros, y de lo contrario imprimir los dobles con la precisión mínima necesaria:

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

Produce:

 232 0.18 1237875192 4.58 0 1.2345 

Y no confía en la manipulación de cadenas.

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

Como se señaló en los comentarios, esta no es la respuesta correcta a la pregunta original.
Dicho esto, es una forma muy útil de formatear números sin ceros finales innecesarios.

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

En breve:

Si desea deshacerse de los ceros finales y los problemas de configuración regional, entonces debe usar:

 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 

Explicación:

Por qué otras respuestas no me sentaron bien:

  • Double.toString() o System.out.println o FloatingDecimal.toJavaFormatString utiliza notaciones científicas si double es menor que 10 ^ -3 o mayor o igual que 10 ^ 7

     double myValue = 0.00000021d; String.format("%s", myvalue); //output: 2.1E-7 
  • al usar %f , la precisión decimal predeterminada es 6, de lo contrario, puede codificarla pero resulta en cero adicionales si tiene menos decimales. Ejemplo:

     double myValue = 0.00000021d; String.format("%.12f", myvalue); //output: 0.000000210000 
  • usando setMaximumFractionDigits(0); o %.0f elimina cualquier precisión decimal, lo cual está bien para enteros / largos pero no para el doble

     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 
  • al usar DecimalFormat, usted es dependiente local. En la configuración regional francesa, el separador decimal es una coma, no un punto:

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

    El uso de la configuración regional INGLÉS asegura que obtenga un punto para el separador decimal, donde sea que se ejecute su progtwig

¿Por qué usar 340 entonces para setMaximumFractionDigits ?

Dos razones :

  • setMaximumFractionDigits acepta un entero pero su implementación tiene un máximo de dígitos permitidos de DecimalFormat.DOUBLE_FRACTION_DIGITS que es igual a 340
  • Double.MIN_VALUE = 4.9E-324 por lo que con 340 dígitos está seguro de no redondear su doble y suelta precisión

Por qué no:

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

Esto debería funcionar con los valores extremos soportados por Double. Rendimientos:

 0.12 12 12.144252 0 

En mi máquina, la siguiente función es aproximadamente 7 veces más rápida que la función proporcionada por la respuesta de JasonD , ya que evita String.format :

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

Mis 2 centavos:

 if(n % 1 == 0) { return String.format(Locale.US, "%.0f", n)); } else { return String.format(Locale.US, "%.1f", n)); } 

Naw, no importa.

La pérdida de rendimiento debido a la manipulación de cadenas es cero.

Y aquí está el código para recortar el final después de %f

 private static String trimTrailingZeros(String number) { if(!number.contains(".")) { return number; } return number.replaceAll("\\.?0*$", ""); } 

Tenga en cuenta que String.format(format, args...) depende de la configuración regional porque formatea usando la configuración regional predeterminada del usuario, es decir, probablemente con comas e incluso espacios dentro como 123 456,789 o 123,456.789 , que pueden no ser exactamente lo que usted esperar.

Puede preferir utilizar String.format((Locale)null, format, args...) .

Por ejemplo,

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

huellas dactilares

 123456,789000 123456,789000 123456.789000 

y esto es lo que hará String.format(format, args...) en diferentes países.

EDITAR Ok, ya que ha habido una discusión sobre formalidades:

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

Hice un DoubleFormatter para convertir eficientemente un gran número de valores dobles en un String bonito / presentable:

 double horribleNumber = 3598945.141658554548844; DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal String beautyDisplay = df.format(horribleNumber); 
  • Si la parte entera de V tiene más de MaxInteger => pantalla V en formato científico (1.2345e + 30), de lo contrario, se mostrará en formato normal 124.45678.
  • el MaxDecimal decide los números de dígitos decimales (recorte con redondeo bancario)

Aquí el código:

 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 (absvEXP_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 (absvEXP_UP) { df.applyPattern("0."+sharpByPrecision+"E0"); } else { df.applyPattern("#,##0."+sharpByPrecision); } } return f.format(v); } /** * Convert "3.1416e+12" to "3.1416e+12" * 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(""); int p1 = s.indexOf('.'); if (p1>0) { resu.append(s.substring(0, p1)); resu.append(""); } else { p1 = 0; } int p2 = s.lastIndexOf('e'); if (p2>0) { resu.append(s.substring(p1, p2)); resu.append(""); resu.append(s.substring(p2, s.length())); resu.append(""); } else { resu.append(s.substring(p1, s.length())); if (p1==0){ resu.append(""); } } return resu.toString(); } } 

Nota: utilicé 2 funciones de la biblioteca GUAVA. Si no usa GUAVA, codifíquelo usted mismo:

 /** * Equivalent to Strings.repeat("#", n) of the Guava library: */ private static String createSharp(int n) { StringBuilder sb = new StringBuilder(); for (int i=0;i 
 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); } } 

Este hará bien el trabajo, sé que el tema es viejo, pero estaba luchando con el mismo problema hasta que llegué a esto. Espero que alguien lo encuentre útil.

  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 
  1. 0 para el número mínimo de dígitos
  2. Renders # dígitos

Respuesta tardía, pero …

Dijiste que elegiste almacenar tus números con el tipo doble . Creo que esta podría ser la raíz del problema porque te obliga a almacenar enteros en dobles (y por lo tanto perder la información inicial sobre la naturaleza del valor). ¿Qué hay de almacenar sus números en instancias de la clase Número (superclase de Doble y Entero) y confiar en el polymorphism para determinar el formato correcto de cada número?

Sé que puede no ser aceptable refactorizar una parte completa de tu código debido a eso, pero podría producir el resultado deseado sin código / colada / análisis adicionales.

Ejemplo:

 import java.util.ArrayList; import java.util.List; public class UseMixedNumbers { public static void main(String[] args) { List listNumbers = new ArrayList(); 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); } } } 

Producirá el siguiente resultado:

 232 0.18 1237875192 4.58 0 1.2345 

Use un DecimalFormat y setMinimumFractionDigits(0)

Aquí hay dos formas de lograrlo. Primero, la forma más corta (y probablemente mejor):

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

Y esta es la forma más larga y probablemente peor:

 public static String formatFloatToString(final float f) { final String s=Float.toString(f); int dotPos=-1; for(int i=0;i 
 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); } 

Tuve que usar esta causa d == (long)d me estaba dando una violación en el informe del sonar

Esto es lo que se me ocurrió:

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

Un trazador de líneas simple, solo se envía a int si realmente necesita

Aquí hay una respuesta que realmente funciona (combinación de diferentes respuestas aquí)

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

Sé que este es un hilo muy viejo … Pero creo que la mejor manera de hacerlo es el siguiente:

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

Salida:

 3.456 something 3.456234523452 something 3.45 something 3.0 something 

El único problema es el último donde .0 no se elimina. Pero si puedes vivir con eso, entonces esto funciona mejor. % .2f lo redondeará a los últimos 2 dígitos decimales. Lo mismo hará DecimalFormat. Si necesita todos los lugares decimales pero no los ceros finales, esto funciona mejor.

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

Esto hará que la cuerda suelte los extremos 0-s.