Buscar..


Introducción

Los números de punto flotante son números que tienen partes fraccionarias (generalmente expresadas con un punto decimal). En Java, hay dos tipos primitivos para números de punto flotante que son float (usa 4 bytes) y double (usa 8 bytes). Esta página de documentación es para detallar con operaciones de ejemplos que se pueden hacer en puntos flotantes en Java.

Comparando valores de punto flotante

Debe tener cuidado al comparar valores de punto float ( float o double ) utilizando operadores relacionales: == != , < Y así sucesivamente. Estos operadores dan resultados de acuerdo con las representaciones binarias de los valores de punto flotante. Por ejemplo:

public class CompareTest {
    public static void main(String[] args) {
        double oneThird = 1.0 / 3.0;
        double one = oneThird * 3;
        System.out.println(one == 1.0);      // prints "false"
    }
}

El cálculo oneThird ha introducido un pequeño error de redondeo, y cuando multiplicamos oneThird por 3 obtenemos un resultado que es ligeramente diferente a 1.0 .

Este problema de representaciones inexactas es más grave cuando intentamos mezclar el double y el float en los cálculos. Por ejemplo:

public class CompareTest2 {
    public static void main(String[] args) {
        float floatVal = 0.1f;
        double doubleVal = 0.1;
        double doubleValCopy = floatVal;

        System.out.println(floatVal);      // 0.1
        System.out.println(doubleVal);     // 0.1
        System.out.println(doubleValCopy); // 0.10000000149011612
        
        System.out.println(floatVal == doubleVal); // false
        System.out.println(doubleVal == doubleValCopy); // false
    }
}

Las representaciones de punto flotante utilizadas en Java para los tipos float y double tienen un número limitado de dígitos de precisión. Para el tipo float , la precisión es de 23 dígitos binarios o aproximadamente 8 dígitos decimales. Para el tipo double , es de 52 bits o alrededor de 15 dígitos decimales. Además de eso, algunas operaciones aritméticas introducirán errores de redondeo. Por lo tanto, cuando un programa compara valores de punto flotante, es una práctica estándar definir un delta aceptable para la comparación. Si la diferencia entre los dos números es menor que el delta, se considera que son iguales. Por ejemplo

if (Math.abs(v1 - v2) < delta)

Ejemplo de comparación de Delta:

public class DeltaCompareExample {

    private static boolean deltaCompare(double v1, double v2, double delta) {
        // return true iff the difference between v1 and v2 is less than delta
        return Math.abs(v1 - v2) < delta;
    }
    
    public static void main(String[] args) {
        double[] doubles = {1.0, 1.0001, 1.0000001, 1.000000001, 1.0000000000001};
        double[] deltas = {0.01, 0.00001, 0.0000001, 0.0000000001, 0};

        // loop through all of deltas initialized above
        for (int j = 0; j < deltas.length; j++) {
            double delta = deltas[j];
            System.out.println("delta: " + delta);

            // loop through all of the doubles initialized above
            for (int i = 0; i < doubles.length - 1; i++) {
                double d1 = doubles[i];
                double d2 = doubles[i + 1];
                boolean result = deltaCompare(d1, d2, delta);

                System.out.println("" + d1 + " == " + d2 + " ? " + result);
                
            }

            System.out.println();
        }
    }
}

Resultado:

delta: 0.01
1.0 == 1.0001 ? true
1.0001 == 1.0000001 ? true
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true

delta: 1.0E-5
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true

delta: 1.0E-7
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true

delta: 1.0E-10
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false

delta: 0.0
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false

También para la comparación de tipos primitivos double y float se puede usar el método de compare estática del tipo de boxeo correspondiente. Por ejemplo:

double a = 1.0;
double b = 1.0001;

System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1

Finalmente, determinar qué deltas son más apropiadas para una comparación puede ser complicado. Un enfoque comúnmente utilizado es seleccionar valores delta que, según nuestra intuición, son correctos. Sin embargo, si conoce la escala y la precisión (verdadera) de los valores de entrada y los cálculos realizados, puede ser posible establecer límites matemáticamente sólidos sobre la precisión de los resultados y, por lo tanto, para los deltas. (Hay una rama formal de Matemáticas conocida como Análisis Numérico que solía enseñarse a científicos computacionales que cubrían este tipo de análisis).

OverFlow y UnderFlow

Tipo de datos flotante

El tipo de datos flotante es un punto flotante IEEE 754 de 32 bits de precisión simple.

Desbordamiento de Float

El valor máximo posible es 3.4028235e+38 , cuando supera este valor produce Infinity

float f = 3.4e38f;
float result = f*2;        
System.out.println(result); //Infinity

Float Bajo Flujo

El valor mínimo es 1.4e-45f, cuando está por debajo de este valor, produce 0.0

    float f = 1e-45f;
    float result = f/1000;
    System.out.println(result);

doble tipo de datos

El tipo de datos doble es un punto flotante IEEE 754 de doble precisión de 64-bit .

Double OverFlow

El valor máximo posible es 1.7976931348623157e+308 , cuando supera este valor produce Infinity

double d = 1e308;
double result=d*2;      
System.out.println(result); //Infinity

Double flujo bajo

El valor mínimo es 4.9e-324, cuando está por debajo de este valor, produce 0.0

    double d = 4.8e-323;
    double result = d/1000;
    System.out.println(result); //0.0

Formato de los valores de punto flotante

Los números de punto flotante se pueden formatear como un número decimal utilizando String.format con el String.format 'f'

    //Two digits in fracttional part are rounded
    String format1 = String.format("%.2f", 1.2399);
    System.out.println(format1); // "1.24"

    // three digits in fractional part are rounded 
    String format2 = String.format("%.3f", 1.2399);
    System.out.println(format2); // "1.240"
    
    //rounded to two digits, filled with zero 
    String format3 = String.format("%.2f", 1.2);
    System.out.println(format3); // returns "1.20"
    
    //rounder to two digits
    String format4 = String.format("%.2f", 3.19999);
    System.out.println(format4); // "3.20"

Los números de punto flotante se pueden formatear como un número decimal usando DecimalFormat

   // rounded with one digit fractional part 
    String format = new DecimalFormat("0.#").format(4.3200);
    System.out.println(format); // 4.3
    
   // rounded with two digit fractional part 
    String format = new DecimalFormat("0.##").format(1.2323000);
    System.out.println(format); //1.23

    // formatting floating numbers to decimal number
    double dv = 123456789;
    System.out.println(dv); // 1.23456789E8
    String format =  new DecimalFormat("0").format(dv);
    System.out.println(format); //123456789

Adherencia estricta a la especificación IEEE

De forma predeterminada, las operaciones de punto flotante en float y double no se ajustan estrictamente a las reglas de la especificación IEEE 754. Se permite que una expresión use extensiones específicas de la implementación para el rango de estos valores; Básicamente permitiendo que sean más precisos de lo requerido.

strictfp desactiva este comportamiento. Se aplica a una clase, interfaz o método, y se aplica a todo lo que contiene, como clases, interfaces, métodos, constructores, inicializadores de variables, etc. Con strictfp , los valores intermedios de una expresión de punto flotante deben estar dentro de el conjunto de valores flotantes o el conjunto de valores dobles. Esto hace que los resultados de tales expresiones sean exactamente aquellos que predice la especificación IEEE 754.

Todas las expresiones constantes son implícitamente estrictas, incluso si no están dentro de un alcance strictfp .

Por lo tanto, strictfp tiene el efecto neto de que algunas veces los cálculos de casos de esquina sean menos precisos, y también puede hacer que las operaciones de punto flotante sean más lentas (ya que la CPU está haciendo más trabajo para garantizar que cualquier precisión adicional nativa no afecte el resultado). Sin embargo, también hace que los resultados sean exactamente iguales en todas las plataformas. Por lo tanto, es útil en cosas como programas científicos, donde la reproducibilidad es más importante que la velocidad.

public class StrictFP { // No strictfp -> default lenient
    public strictfp float strict(float input) {
        return input * input / 3.4f; // Strictly adheres to the spec.
                                     // May be less accurate and may be slower.
    }

    public float lenient(float input) {
        return input * input / 3.4f; // Can sometimes be more accurate and faster,
                                     // but results may not be reproducable.
    }

    public static final strictfp class Ops { // strictfp affects all enclosed entities
        private StrictOps() {}

        public static div(double dividend, double divisor) { // implicitly strictfp
            return dividend / divisor;
        }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow