Pregunta Cómo redondear un número a n lugares decimales en Java


Lo que me gustaría es un método para convertir un doble en una cadena que redondea usando el método de la mitad arriba, es decir, si el decimal que se redondeará es 5, siempre se redondea al número anterior. Este es el método estándar de redondeo que la mayoría de la gente espera en la mayoría de las situaciones.

También me gustaría que solo se muestren dígitos significativos, es decir, no debería haber ningún cerco final.

Sé que un método para hacer esto es usar el String.format método:

String.format("%.5g%n", 0.912385);

devoluciones:

0.91239

Lo cual es genial, sin embargo, siempre muestra números con 5 lugares decimales, incluso si no son significativos:

String.format("%.5g%n", 0.912300);

devoluciones:

0.91230

Otro método es usar el DecimalFormatter:

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

devoluciones:

0.91238

Sin embargo, como puede ver, esto utiliza el redondeo a la mitad o incluso el redondeo. Es decir redondeará hacia abajo si el dígito anterior es par. Lo que me gustaría es esto:

0.912385 -> 0.91239
0.912300 -> 0.9123

¿Cuál es la mejor manera de lograr esto en Java?


1021
2017-09-30 16:01


origen


Respuestas:


Utilizar setRoundingMode, selecciona el RoundingMode explique explícitamente su problema con la ronda de medios pares, luego use el patrón de formato para su salida requerida.

Ejemplo:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

da la salida:

12
123.1235
0.23
0.1
2341234.2125

583
2017-09-30 16:14



Asumiendo value es un double, tu puedes hacer:

(double)Math.round(value * 100000d) / 100000d

Eso es para 5 dígitos de precisión. El número de ceros indica la cantidad de decimales.


397
2017-09-30 16:07



new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

te conseguirá un BigDecimal. Para sacarle la cadena, solo llame BigDecimales toString método, o el toPlainString método para Java 5+ para una cadena de formato simple.

Programa de ejemplo:

package trials;
import java.math.BigDecimal;

public class Trials {

    public static void main(String[] args) {
        int yourScale = 10;
        System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
    }

164
2017-09-30 18:33



También puedes usar el

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

para asegurarse de tener los 0 finales.


98
2017-09-30 16:58



Como algunos otros han notado, la respuesta correcta es usar cualquiera DecimalFormat o BigDecimal. El punto flotante no tener lugares decimales por lo que no es posible redondear / truncar a un número específico de ellos en primer lugar. Tienes que trabajar en una base decimal, y eso es lo que hacen esas dos clases.

Estoy publicando el siguiente código como un contraejemplo para todas las respuestas en este hilo y, de hecho, en todo StackOverflow (y en otros lugares) que recomiendan multiplicación seguida de truncamiento seguido de división. Incumbe a los defensores de esta técnica explicar por qué el siguiente código produce el resultado incorrecto en más del 92% de los casos.

public class RoundingCounterExample
{

    static float roundOff(float x, int position)
    {
        float a = x;
        double temp = Math.pow(10.0, position);
        a *= temp;
        a = Math.round(a);
        return (a / (float)temp);
    }

    public static void main(String[] args)
    {
        float a = roundOff(0.0009434f,3);
        System.out.println("a="+a+" (a % .001)="+(a % 0.001));
        int count = 0, errors = 0;
        for (double x = 0.0; x < 1; x += 0.0001)
        {
            count++;
            double d = x;
            int scale = 2;
            double factor = Math.pow(10, scale);
            d = Math.round(d * factor) / factor;
            if ((d % 0.01) != 0.0)
            {
                System.out.println(d + " " + (d % 0.01));
                errors++;
            }
        }
        System.out.println(count + " trials " + errors + " errors");
    }
}

Salida de este programa:

10001 trials 9251 errors

EDITAR: Para abordar algunos comentarios a continuación redirigí la parte del módulo del ciclo de prueba utilizando BigDecimal y new MathContext(16) para la operación del módulo de la siguiente manera:

public static void main(String[] args)
{
    int count = 0, errors = 0;
    int scale = 2;
    double factor = Math.pow(10, scale);
    MathContext mc = new MathContext(16, RoundingMode.DOWN);
    for (double x = 0.0; x < 1; x += 0.0001)
    {
        count++;
        double d = x;
        d = Math.round(d * factor) / factor;
        BigDecimal bd = new BigDecimal(d, mc);
        bd = bd.remainder(new BigDecimal("0.01"), mc);
        if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
        {
            System.out.println(d + " " + bd);
            errors++;
        }
    }
    System.out.println(count + " trials " + errors + " errors");
}

Resultado:

10001 trials 4401 errors

75
2017-10-02 03:18



Supongamos que tiene

double d = 9232.129394d;

puedes usar BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

o sin BigDecimal

d = Math.round(d*100)/100.0d;

con ambas soluciones d == 9232.13


71
2018-01-28 09:52



Puede usar la clase DecimalFormat.

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

52
2017-09-29 07:01



Cómo funciona Java de Real publicaciones esta solución, que también es compatible para versiones anteriores a Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();

34
2017-09-01 11:14