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


496

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

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

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?

Yo he tratado String.format("%f", value) , que está cerca, excepto que obtengo 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 0
1.2345

Claro que puedo escribir una función para recortar esos ceros, pero eso es 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 localización (ver respuestas a continuación).


Si todo lo que quieres son enteros, ¿por qué no usar un largo? Obtiene más explosión a 2 ^ 63-1, sin formato incómodo y mejor rendimiento.
basszero

14
Porque algunos valores son en realidad dobles
Pyrolistical

2
Algunos casos en los que se produjo este problema fue un error solucionado en JDK 7: stackoverflow.com/questions/7564525/…
Pyrolistic

¿Soy solo yo o JavaScript es 100% mejor en la conversión de números a cadenas que Java?
Andy

System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
Shayan Amani

Respuestas:


399

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 se basa en la manipulación de cadenas.


99
De acuerdo, esta es una mala respuesta, no la use. No funciona con un valor doublemayor que el máximo int. Incluso con longeso todavía fallaría para un gran número. Además, devolverá una Cadena en forma exponencial, por ejemplo, "1.0E10", para valores grandes, que probablemente no sea lo que el autor de la pregunta desea. Use en %flugar de %sen la segunda cadena de formato para arreglar eso.
jlh

26
El OP declaró explícitamente que no querían que la salida se formateara usando %f. La respuesta es específica para la situación descrita y el resultado deseado. El OP sugirió que su valor máximo era un int sin signo de 32 bits, lo que entendí que intera aceptable (el unsigned no existía realmente en Java y ningún ejemplar era problemático), pero cambiar inta longes una solución trivial si la situación es diferente.
JasonD

1
¿Dónde en la pregunta dice que no debería hacer eso?
JasonD

66
String.format("%s",d)??? Hable sobre gastos generales innecesarios. Uso Double.toString(d). Lo mismo para el otro: Long.toString((long)d).
Andreas

15
El problema es que %sno funciona con configuraciones regionales. En alemán, usamos un "," en lugar de un "." en números decimales Mientras String.format(Locale.GERMAN, "%f", 1.5)devuelve "1.500000", String.format(Locale.GERMAN, "%s", 1.5)devuelve "1.5" - con un ".", Que es falso en idioma alemán. ¿Existe una versión local dependiente de "% s" también?
Felix Edelmann

414
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.


16
Una nota importante aquí es que 1.1 se formateará correctamente como "1.1" sin ceros finales.
Steve Pomeroy

53
Y si desea un número específico de ceros finales (por ejemplo, si está imprimiendo cantidades de dinero), puede usar '0' en lugar de '#' (es decir, nuevo formato DecimalFormat ("0.00"). (Cantidad);) no es lo que OP quería, pero puede ser útil como referencia.
TJ Ellis

22
Sí, como autor original de la pregunta, esta es la respuesta INCORRECTA. Es curioso cuántos votos hay. El problema con esta solución es que se redondea arbitrariamente a 2 decimales.
Pirolítico

11
@Mazyod porque siempre puedes pasar en un punto flotante con más decimales que el formato. Eso es escribir código que funcionará la mayor parte del tiempo pero no cubrirá todos los casos extremos.
Pirolítico

15
@Pyrolistical - En mi humilde opinión, hay muchos votos a favor porque, aunque esta es la solución incorrecta para usted, es la solución correcta para más del 99% de los que encuentran estas preguntas y respuestas: por lo general, los últimos dígitos de un doble son "ruido", que desordenan la salida, interfiriendo con la legibilidad. Por lo tanto, el programador determina cuántos dígitos son beneficiosos para la persona que lee la salida y especifica esa cantidad. Una situación común es que se han acumulado pequeños errores matemáticos, por lo que un valor puede ser 12.000000034, pero preferiría redondear a 12 y mostrar de forma compacta como "12". Y "12.340000056" => "12.34".
ToolmakerSteve

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

13
Eso es correcto pero siempre imprime ceros finales incluso si no hay una parte fraccional. String.format ("%. 2f, 1.0005) imprime 1.00 y no 1. ¿Hay algún especificador de formato para no imprimir una parte fraccional si no existe?
Emre Yazici

87
Abajo votó, ya que la pregunta es eliminar todos los ceros finales y esta respuesta siempre dejará dos puntos flotantes independientemente de ser cero.
Zulaxia

El DecimalFormat fue un buen truco, aunque terminé usando este para mi situación (temporizador de nivel de juego) ya que los ceros finales se veían mejor.
Timothy Lee Russell, el

2
Creo que puedes manejar los ceros finales correctamente usando g en lugar de f.
Peter Ajtai

3
Usé esta solución en un sistema de producción con "% .5f", y es realmente muy mala, no la use ... porque imprimió esto: 5.12E-4 en lugar de 0.000512
finalice el

87

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.printlno FloatingDecimal.toJavaFormatStringutiliza notación científica si doble es menos de 10 ^ -3 o mayor que 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 resulta en ceros adicionales agregados si tiene menos decimales. Ejemplo:

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • usando setMaximumFractionDigits(0);o %.0feliminando 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 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 asegura que obtenga un punto para el separador decimal, donde sea que se ejecute su programa

¿Por qué usar 340 entonces para setMaximumFractionDigits?

Dos razones :

  • setMaximumFractionDigitsacepta un número entero pero su implementación tiene un máximo de dígitos permitidos de los DecimalFormat.DOUBLE_FRACTION_DIGITScuales es igual a 340
  • Double.MIN_VALUE = 4.9E-324 así que con 340 dígitos está seguro de no redondear su precisión doble y suelta

Esto no funciona para enteros, por ejemplo, "2" se convierte en "2".
kap

Gracias, arreglé la respuesta usando el patrón en 0lugar de#.
JBE

No está utilizando la constante, DecimalFormat.DOUBLE_FRACTION_DIGITSpero está utilizando el valor 340, que luego proporciona un comentario para mostrar que es igual DecimalFormat.DOUBLE_FRACTION_DIGITS. ¿Por qué no usar la constante?
Maarten Bodewes

1
Debido a que este atributo no es público ... es "paquete amigable"
JBE

44
¡Gracias! De hecho, esta respuesta es la única que realmente coincide con todos los requisitos mencionados en la pregunta: no muestra ceros innecesarios, no redondea los números y depende de la configuración regional. ¡Excelente!
Felix Edelmann

26

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 admitidos por Double. Rendimientos:

0.12
12
12.144252
0

2
Prefiero esta respuesta por la cual no necesitamos hacer conversión de tipo.
Jeff T.

Breve explicación: "%s"básicamente llama d.toString()pero no funciona con into si d==null.
Neph

24

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

1
Hmm, esto no tiene en cuenta las configuraciones regionales, pero tampoco JasonD's.
TWiStErRob

22

Mis 2 centavos:

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

2
O simplemente return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Emperor

fallar cuando 23.00123 ==> 23.00
aswzen

11

No, 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 %f

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

    return number.replaceAll("\\.?0*$", "");
}

77
Voté en contra porque su solución no es la mejor opción. Echa un vistazo a String.format. Debe utilizar el tipo de formato correcto, flotante en esta instancia. Mira mi respuesta anterior.
jjnguy 01 de

66
He votado porque tengo el mismo problema, y ​​nadie aquí parece entender el problema.
Obay

1
Votado como DecimalFormat mencionado en la publicación de Tom es exactamente lo que estabas buscando. Elimina los ceros con bastante eficacia.
Steve Pomeroy

44
A lo anterior, ¿tal vez quiera recortar los ceros SIN redondear? PS @Pyrolistical, seguramente puede usar number.replaceAll (".? 0 * $", ""); (después contiene (".") por supuesto)
Rehno Lindeque

1
Ok, entonces, ¿cómo podrías lograr mi objetivo con DecimalFormat?
Pyrolistic

8

Use un DecimalFormatysetMinimumFractionDigits(0)


Agregaría setMaximumFractionDigits(2)y setGroupingUsed(false)(OP no lo menciona, pero por ejemplo parece que es obligatorio). Además, un pequeño caso de prueba no duele ya que es trivial en este caso. Aún así, como creo que es la solución más simple, un
voto a favor

6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}

1
Creo que te seguiré en esto: D
aeracode

5

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

Puede preferir usar 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 String.format(format, args...)hará 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;
}

1
debe agregar esto como un comentario a la respuesta aceptada
Pyrolistical

Los comentarios no permiten la longitud o el formato de este apéndice. Como agrega información posiblemente útil, creo que debería permitirse tal cual en lugar de eliminarse.
Terry Jan Reedy

5

Hice una DoubleFormatterconversión eficiente de una gran cantidad de valores dobles a una cadena agradable / 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 => muestra V en formato científico (1.2345e + 30), de lo contrario, muestra en formato normal 124.45678.
  • 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 (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: Usé 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<n;i++) {
        sb.append('#');
    }
    return sb.toString();
}

1
Si conoce la precisión, utilice un BigDecimal. Ver docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pyrolistic

5

Este hará el trabajo bien, sé que el tema es antiguo, 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);
    }

5
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 para un número mínimo de dígitos
  2. Representa # dígitos

Si bien esto puede proporcionar una solución a la pregunta, es una buena práctica agregar una breve explicación para que la comunidad se beneficie (y aprenda) de la respuesta
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);
    }
}

en su lugar, solo debe buscar el primer dígito distinto de cero desde la derecha y luego usar la subcadena (y también verificar que la cadena contiene ".", por supuesto). de esta manera, no podrás crear tantas cadenas temporales en el camino.
Desarrollador de Android

3

Respuesta tardía pero ...

Dijiste que eliges almacenar tus números con el tipo doble . Creo que esta podría ser la raíz del problema porque te obliga a almacenar números 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 del Número? clase (superclase de Double y Integer) y confiar en el polimorfismo para determinar el formato correcto de cada número?

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

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

javascript hizo la misma elección por cierto :)
Pyrolistical

@Pirolística ¿Puedes explicar un poco más tu afirmación? No está del todo claro para mí ... :)
Visto el

2

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 forro simple, solo se envía a int si realmente necesita


1
Repitiendo lo que Felix Edelmann dijo en otra parte: esto creará una cadena independiente de la configuración regional, que puede no ser siempre apropiada para el usuario.
JJ Brown

punto justo, para mi caso de uso esto no fue un problema, no estoy completamente seguro en este momento, pero creo que uno podría usar String.format (con la configuración regional deseada) en lugar de valueOf
keisar

2

Precio de formato con agrupación, redondeo, sin ceros innecesarios (en doble).

Reglas:

  1. No hay ceros al final ( 2.0000 = 2; 1.0100000 = 1.01)
  2. Dos dígitos como máximo después de un punto ( 2.010 = 2.01; 0.20 = 0.2)
  3. Redondeo después del segundo dígito después de un punto ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Devoluciones 0( null/-0 = 0)
  5. Agrega $(= $56/-$56 )
  6. Agrupación ( 101101.02 = $101,101.02)

Más ejemplos:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Escrito en Kotlin como una divertida extensión de Double (causa utilizada en Android), pero se puede convertir a Java fácilmente, ya que se usaron clases 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)
    }
}

Cómo utilizar:

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

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

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


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

Tuve que usar esta causa d == (long)dme estaba infringiendo en el informe del sonar


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

Este es el resultado de algunas pruebas:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4

¿Qué pasa con 1.23450000?
Alex78191

1.23450000 => 1.23
Ahmed Mihoub

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 aquí está 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<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;
  }

1
a veces, cuando haces las cosas más simples, el código detrás es más complejo y menos optimizado ... pero sí, puedes usar muchas funciones API integradas ...
desarrollador de Android

1
Debe comenzar con una simple y una vez que haya determinado que tiene un problema de rendimiento, entonces y solo entonces debe optimizar. El código es para que el humano lea una y otra vez. Hacer que funcione rápido es secundario. Al no utilizar la API estándar siempre que sea posible, es más probable que introduzca errores y solo dificulte el cambio en el futuro.
Pirolítico

3
Yo diría que el código que escribes así NO será más rápido. El JVM es muy inteligente y en realidad no sabes qué tan rápido o lento es algo hasta que lo perfilas. Los problemas de rendimiento se pueden detectar y solucionar cuando se convierte en un problema. No debe optimizarlo prematuramente. Escriba el código para que la gente lo lea, no para cómo imagina que la máquina lo va a ejecutar. Una vez que se convierta en un problema de rendimiento, vuelva a escribir el código con un generador de perfiles.
Pirolítico

2
Alguien más editó la respuesta para mejorar el formato del código. Estaba revisando varias docenas de ediciones para su aprobación e iba a aprobar su edición aquí, pero las ediciones eran inconsistentes, así que las arreglé. También mejoré la gramática de los fragmentos de texto.
Steve Vinoski

1
No entiendo. Si dijiste que el formato no importaba, ¿por qué pasaste el tiempo para cambiarlo?
OrhanC1

0

Para Kotlin puedes usar extensiones como:

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

0

Aquí hay otra respuesta que tiene una opción para agregar decimal SOLO SI el decimal no fuera cero.

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

-1

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*$", "");
}

1
no reemplazó el PUNTO, por ejemplo, "100.0" se convertirá a "100".
VinceStyling

si (f == (int) f) se encarga de eso.
Martin Klosi

2
Falla en f = 9999999999.00
Dawood ibn Kareem

-4

Sé que este es un hilo muy antiguo ... 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 donde no se elimina .0. Pero si puede vivir con eso, entonces esto funciona mejor. % .2f lo redondeará a los últimos 2 dígitos decimales. También lo hará DecimalFormat. Si necesita todos los lugares decimales pero no los ceros finales, entonces esto funciona mejor.


2
El formato decimal con formato de "#. ##" no mantendrá 0 adicional si no son necesarios: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));se imprimirá1
Aleks G,

ese es mi punto. ¿Qué pasa si desea que se muestre el 0.0005 si hay alguno? Lo redondeará 2 dígitos decimales.
sethu

El OP pregunta cómo imprimir valores enteros almacenados en dobles :)
Aleks G

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

Esto hará que la cadena deje caer los colas 0-s.


1
Esta es una buena solución a la pregunta, si solo estuvieran interesados ​​en eliminar los ceros finales, ¿cómo cambiaría su código para recortar también un punto decimal final? es decir, "1."
bakoyaro

29
Tenga cuidado, su solución convertirá 1000 en 1, lo cual está mal.
Aleks G
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.