Pregunta

public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

El código de arriba imprime:

11.399999999999

¿Cómo puedo conseguir que esto acaba de imprimir (o ser capaz de utilizar como) 11.4?

¿Fue útil?

Solución

Como otros han mencionado, es probable que desee utilizar el BigDecimal clase, si usted quiere tener una representación exacta de 11.4.

Ahora, una pequeña explicación de por qué ocurre esto:

El float y double los tipos primitivos en Java son de punto flotante los números, donde el número se almacena como una representación binaria de una fracción y un exponente.

Más específicamente, un punto flotante de precisión doble valor, tales como la double el tipo es un valor de 64 bits, donde:

  • 1 bit indica el signo (positivo o negativo).
  • 11 bits para el exponente.
  • 52 bits para los dígitos significativos (la parte fraccionaria como un sistema binario).

Estas piezas se combinan para producir un double la representación de un valor.

(Fuente: Wikipedia:De doble precisión)

Para una descripción detallada de cómo los valores de punto flotante se manejan en Java, consulte la La sección 4.2.3:Los Tipos de Punto flotante, los Formatos y los Valores de la de la Especificación del Lenguaje Java.

El byte, char, int, long tipos de de punto fijo los números, que son exactas representions de números.A diferencia de punto fijo de números, los números de punto flotante, algunas veces (seguro asumir que "la mayoría del tiempo") no ser capaz de devolver una representación exacta de un número.Esta es la razón por la que terminan con 11.399999999999 como resultado de 5.6 + 5.8.

Cuando se requiere un valor exacto, tales como 1.5 o 150.1005, usted querrá usar uno de los fijos de los tipos de punto, que será capaz de representar el número exacto.

Como ha sido mencionado varias veces ya, Java tiene un BigDecimal la clase que se encargará de números muy grandes y muy pequeñas cantidades.

A partir de la API de Java de Referencia para la BigDecimal clase:

Inmutable, de precisión arbitraria firmado decimal números.Un BigDecimal consta de un de precisión arbitraria entero sin escala y un valor entero de 32 bits de escala.Si cero o positivo, la escala es la número de dígitos a la derecha de la punto decimal.Si es negativo, el sin escala de valor del número es multiplicado por diez a la potencia de la la negación de la escala.El valor de el número representado por el BigDecimal es por lo tanto (unscaledValue × 10^-escala).

Ha habido muchas preguntas sobre el Desbordamiento de Pila relativo a la cuestión de números de punto flotante y su precisión.Aquí está una lista de preguntas relacionadas que puedan ser de su interés:

Si usted realmente desea conseguir abajo para los detalles nitty gritty de números de punto flotante, echa un vistazo a Lo Que Cada Equipo Científico Debe Saber Sobre La Aritmética De Punto Flotante.

Otros consejos

Cuando ingresa un número doble, por ejemplo, 33.33333333333333, el valor que obtiene es en realidad el valor de doble precisión representable más cercano, que es exactamente:

33.3333333333333285963817615993320941925048828125

Dividiendo eso por 100 da:

0.333333333333333285963817615993320941925048828125

que tampoco es representable como un número de doble precisión, por lo que nuevamente se redondea al valor representable más cercano, que es exactamente:

0.3333333333333332593184650249895639717578887939453125

Cuando imprime este valor, se redondea una vez más a 17 dígitos decimales, dando:

0.33333333333333326

Si solo desea procesar valores como fracciones, puede crear una clase de fracciones que contenga un campo numerador y denominador.

Escriba métodos para sumar, restar, multiplicar y dividir, así como un método toDouble. De esta forma, puede evitar flotadores durante los cálculos.

EDITAR: Implementación rápida,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}

Observe que tendría el mismo problema si utilizara la aritmética decimal de precisión limitada y quisiera tratar con 1/3: 0.333333333 * 3 es 0.999999999, no 1.00000000.

Desafortunadamente, 5.6, 5.8 y 11.4 simplemente no son números redondos en binario, porque involucran quintos. Entonces, la representación flotante de ellos no es exacta, al igual que 0.3333 no es exactamente 1/3.

Si todos los números que usa son decimales no recurrentes y desea resultados exactos, use BigDecimal. O como han dicho otros, si sus valores son como el dinero en el sentido de que todos son múltiplos de 0.01, o 0.001, o algo así, multiplique todo por una potencia fija de 10 y use int o long (la suma y la resta son trivial: cuidado con la multiplicación).

Sin embargo, si está satisfecho con el binario para el cálculo, pero solo desea imprimir las cosas en un formato un poco más amigable, intente java.util.Formatter o String.format. En la cadena de formato, especifique una precisión menor que la precisión completa de un doble. Para 10 cifras significativas, digamos, 11.399999999999 es 11.4, por lo que el resultado será casi tan preciso y más legible para los humanos en los casos en que el resultado binario esté muy cerca de un valor que requiera solo unos pocos decimales.

La precisión para especificar depende un poco de la cantidad de matemáticas que haya hecho con sus números; en general, cuanto más lo haga, más error se acumulará, pero algunos algoritmos lo acumulan mucho más rápido que otros (se llaman < !> "; inestable &"; en oposición a & "; estable &"; con respecto a los errores de redondeo). Si todo lo que está haciendo es agregar algunos valores, entonces supongo que soltar solo un decimal de precisión resolverá las cosas. Experimento.

Es posible que desee considerar el uso de la clase java.math.BigDecimal de java si realmente necesita matemática de precisión. Aquí hay un buen artículo de Oracle / Sun sobre el caso de BigDecimal . Si bien nunca puede representar 1/3 como alguien mencionó, puede tener el poder de decidir exactamente qué tan preciso desea que sea el resultado. setScale () es tu amigo .. :)

Ok, porque tengo mucho tiempo libre en este momento, aquí hay un ejemplo de código relacionado con su pregunta:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

y para conectar mi nuevo idioma favorito, Groovy, aquí hay un ejemplo más ordenado de lo mismo:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333

Estoy bastante seguro de que podrías haberlo convertido en un ejemplo de tres líneas. :)

Si quieres precisión exacta, usa BigDecimal. De lo contrario, puede usar ints multiplicados por 10 ^ cualquier precisión que desee.

Como otros han señalado, no todos los valores decimales se pueden representar como binarios, ya que el decimal se basa en potencias de 10 y el binario se basa en potencias de dos.

Si la precisión es importante, usa BigDecimal, pero si solo quieres una salida amigable:

System.out.printf("%.2f\n", total);

Te dará:

11.40

Estás corriendo contra la limitación de precisión del tipo doble.

Java.Math tiene algunas facilidades aritméticas de precisión arbitraria.

No puede, porque 7.3 no tiene una representación finita en binario. Lo más cercano que puede obtener es 2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280.

Eche un vistazo a http://docs.python.org/tutorial/floatingpoint.html para una explicación más detallada. (Está en el sitio web de Python, pero Java y C ++ tienen el mismo & Quot; problem & Quot ;.)

La solución depende de cuál sea exactamente su problema:

  • Si es que simplemente no te gusta ver todos esos dígitos de ruido, entonces arregla el formato de tu cadena. No muestre más de 15 dígitos significativos (o 7 para flotante).
  • Si es que la inexactitud de sus números está rompiendo cosas como " if " , entonces debe escribir if (abs (x - 7.3) < TOLERANCE) en lugar de if (x == 7.3).
  • Si estás trabajando con dinero, entonces lo que probablemente quieras es un punto fijo decimal. Almacene un número entero de centavos o cualquiera que sea la unidad más pequeña de su moneda.
  • (MUY PROBABLE) Si necesita más de 53 bits significativos (15-16 dígitos significativos) de precisión, utilice un tipo de punto flotante de alta precisión, como BigDecimal.
private void getRound() {
    // this is very simple and interesting 
    double a = 5, b = 3, c;
    c = a / b;
    System.out.println(" round  val is " + c);

    //  round  val is  :  1.6666666666666667
    // if you want to only two precision point with double we 
            //  can use formate option in String 
           // which takes 2 parameters one is formte specifier which 
           // shows dicimal places another double value 
    String s = String.format("%.2f", c);
    double val = Double.parseDouble(s);
    System.out.println(" val is :" + val);
    // now out put will be : val is :1.67
}

Use java.math.BigDecimal

Los dobles son fracciones binarias internamente, por lo que a veces no pueden representar fracciones decimales al decimal exacto.

Multiplica todo por 100 y guárdalo en centavos.

Las computadoras almacenan números en binario y en realidad no pueden representar números como 33.333333333 o 100.0 exactamente. Esta es una de las cosas difíciles sobre el uso de dobles. Tendrá que redondear la respuesta antes de mostrársela a un usuario. Afortunadamente, en la mayoría de las aplicaciones, de todos modos no necesita tantos decimales.

Números de punto flotante se diferencian de los números reales en que para cualquier número de punto flotante, hay una mayor siguiente número de punto flotante.Mismo como enteros.No hay ningún número entero entre 1 y 2.

No hay ninguna manera de representar 1/3 como un flotador.Hay un flotador debajo de ella y hay una flotar por encima de ella, y hay una cierta distancia entre ellos.Y 1/3 es en ese espacio.

Apfloat para Java reclamaciones para trabajar con precisión arbitraria de números de punto flotante, pero nunca la he utilizado.Probablemente vale la pena un vistazo.http://www.apfloat.org/apfloat_java/

Una pregunta similar se le preguntó aquí antes Java de punto flotante de alta precisión de la biblioteca

Los dobles son aproximaciones de los números decimales en su fuente Java. Estás viendo la consecuencia de la falta de coincidencia entre el doble (que es un valor codificado en binario) y tu fuente (que está codificada en decimal).

Java produce la aproximación binaria más cercana. Puede usar java.text.DecimalFormat para mostrar un valor decimal más atractivo.

Use un BigDecimal. Incluso le permite especificar reglas de redondeo (como ROUND_HALF_EVEN, que minimizará el error estadístico al redondear al vecino par si ambos están a la misma distancia; es decir, tanto 1.5 como 2.5 redondean a 2).

Echa un vistazo a BigDecimal, maneja problemas relacionados con la aritmética de coma flotante como esa.

La nueva llamada se vería así:

term[number].coefficient.add(co);

Utilice setScale () para establecer el número de precisión de lugar decimal que se utilizará.

¿Por qué no usar el método round () de la clase Math?

// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4

Si no tiene otra opción que usar valores dobles, puede usar el siguiente código.

public static double sumDouble(double value1, double value2) {
    double sum = 0.0;
    String value1Str = Double.toString(value1);
    int decimalIndex = value1Str.indexOf(".");
    int value1Precision = 0;
    if (decimalIndex != -1) {
        value1Precision = (value1Str.length() - 1) - decimalIndex;
    }

    String value2Str = Double.toString(value2);
    decimalIndex = value2Str.indexOf(".");
    int value2Precision = 0;
    if (decimalIndex != -1) {
        value2Precision = (value2Str.length() - 1) - decimalIndex;
    }

    int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
    sum = value1 + value2;
    String s = String.format("%." + maxPrecision + "f", sum);
    sum = Double.parseDouble(s);
    return sum;
}

No desperdicies tu efford usando BigDecimal. En el 99.99999% de los casos no lo necesita. El tipo java doble es de origen aproximado, pero en casi todos los casos, es lo suficientemente preciso. Tenga en cuenta que tiene un error en el decimocuarto dígito significativo. ¡Esto es realmente insignificante!

Para obtener un buen resultado de uso:

System.out.printf("%.2f\n", total);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top