Pregunta

Un doble de 64 bits puede representar un número entero +/- 253 exactamente

Dado este hecho, elijo utilizar un tipo doble como tipo único para todos mis tipos, ya que mi entero más grande es de 32 bits sin signo.

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

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

Yo he tratado String.format("%f", value), lo cual está cerca, excepto que obtengo muchos ceros finales para valores pequeños.

Aquí hay un ejemplo de salida de of %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

Seguro que puedo escribir una función para recortar esos ceros, pero eso supone una gran pérdida de rendimiento debido a la manipulación de cadenas.¿Puedo hacerlo mejor con otro código de formato?

EDITAR

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

EDITAR 2

Tenga en cuenta que String.format(format, args...) es dependiente de la localidad (ver respuestas a continuación).

¿Fue útil?

Solución

Si la idea es imprimir números enteros almacenados como dobles como si fueran números enteros, y de otra manera imprimir los dobles con el mínimo precisión 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 se basa en la manipulación de cadenas.

Otros consejos

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

Como se señaló en los comentarios, esto no es la respuesta correcta a la pregunta original.
Dicho esto, es una manera muy útil para dar formato a números sin ceros de la izquierda innecesarios.

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

En breve:

Si desea deshacerse de los ceros finales y los problemas de configuración regional, 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 convenían:

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

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
    
  • mediante el uso %f, la precisión decimal predeterminada es 6; de lo contrario, puede codificarla, pero se agregarán ceros adicionales si tiene menos decimales.Ejemplo :

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

    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 utilizar DecimalFormat, eres dependiente local.En francés, 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 garantiza que obtenga un punto para el separador decimal, dondequiera que se ejecute su programa.

¿Por qué usar 340 entonces para setMaximumFractionDigits ?

Dos razones :

  • setMaximumFractionDigits acepta un número 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 así que con 340 dígitos seguro que no redondeas tu precisión doble y suelta

¿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 apoyados por doble. Los rendimientos:

0.12
12
12.144252
0

Mis 2 centavos:

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

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

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

No, no importa.

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*$", "");
}
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}

Tenga en cuenta que String.format(format, args...) es dependiente de la localización , ya que da formato a usando regional predeterminada del usuario, que es, probablemente con comas e incluso espacios en el interior como 123 456 789 o 123,456.789 , que no puede ser exactamente lo que usted espera.

Es posible que prefiera 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));

impresiones

123456,789000
123456,789000
123456.789000

y esto es lo que va a hacer String.format(format, args...) en diferentes países.

EDITAR Ok, ya que ha habido una discusión acerca de los trámites:

    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 de manera eficiente un gran número de valores dobles para un buen Cadena / 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 mostrará en formato normal de 124.45678.
  • el MaxDecimal decidir el número de dígitos decimales (Trim con bancaria redondeo)

A continuación, 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 (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();
    }
}

Nota: He usado 2 funciones de la biblioteca de guayaba. Si no se utiliza la guayaba, el código 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<n;i++) {
        sb.append('#');
    }
    return sb.toString();
}

Utilice un DecimalFormat y setMinimumFractionDigits(0)

Esto va a hacer el trabajo muy bien, sé que el tema es viejo, pero yo estaba luchando con el mismo problema hasta que llegué a esto. Espero que alguien sea de utilidad.

    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. muestra # dígitos
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);
    }
}

respuesta tardía pero ...

Usted dijo elija para almacenar sus números con el tipo doble . Creo que esto podría ser la raíz del problema, ya que obliga a almacenar enteros en dobles (y por lo tanto perder la información inicial acerca de la naturaleza del valor). ¿Qué pasa con el almacenamiento de sus números en las instancias de la clase (superclase de ambos dobles y enteros) y se basan en el polimorfismo para determinar el formato correcto de cada número?

Sé que puede no ser aceptable para refactorizar toda una parte de su código, debido a eso, sino que podría producir la salida deseada sin código adicional / fundición / análisis.

Ejemplo:

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

}

producirá el siguiente resultado:

232
0.18
1237875192
4.58
0
1.2345
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);
}

he tenido que utilizar esta causa d == (long)d me estaba dando violación en el informe de 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);
  }

Una sola línea simple, solo se lanza a int si realmente es necesario

Aquí hay dos maneras de lograrlo. En primer lugar, el camino más corto (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 aquí está el más largo y probablemente peor manera:

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

Esta es 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 esto es un hilo muy viejo .. Pero creo que la mejor manera de hacerlo es la 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 en 0.0 no se eliminan. Pero si usted es capaz de vivir con eso, entonces esto funciona mejor. .2f% se redondea a los 2 últimos dígitos decimales. Así se DecimalFormat. Si necesita todas las cifras decimales, pero no los ceros a la derecha, entonces esto funciona mejor.

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

Esto hará que la cadena de dejar caer el tizón 0-s.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top