c++ - normalizada - punto flotante ieee 754 ejemplos




Igualdad de punto flotante (4)

Es de conocimiento común que hay que tener cuidado al comparar los valores de coma flotante. Usualmente, en lugar de usar == , usamos algunas pruebas de igualdad basadas en epsilon o ULP.

Sin embargo, me pregunto, ¿hay algún caso, cuando se usa == está perfectamente bien?

Mire este fragmento simple, ¿qué casos tienen garantía de éxito?

void fn(float a, float b) {
    float l1 = a/b;
    float l2 = a/b;

    if (l1==l1) { }        // case a)
    if (l1==l2) { }        // case b)
    if (l1==a/b) { }       // case c)
    if (l1==5.0f/3.0f) { } // case d)
}

int main() {
    fn(5.0f, 3.0f);
}

Nota: he verificado this y this , pero no cubren (todos) mis casos.

Nota 2: parece que tengo que agregar algo más de información, por lo que las respuestas pueden ser útiles en la práctica: me gustaría saber:

  • lo que dice el estándar de C ++
  • ¿Qué sucede si una implementación en C ++ sigue a IEEE-754?

Esta es la única declaración relevante que encontré en el borrador del estándar actual :

La representación del valor de los tipos de coma flotante está definida por la implementación. [Nota: este documento no impone requisitos sobre la precisión de las operaciones de coma flotante; ver también [support.limits]. - nota final]

Entonces, ¿significa esto que incluso el "caso a)" se define la implementación? Quiero decir, l1==l1 es definitivamente una operación de coma flotante. Entonces, si una implementación es "inexacta", ¿podría l1==l1 ser falso?

Creo que esta pregunta no es un duplicado de this . Esa pregunta no aborda ninguno de los casos que estoy preguntando. El mismo tema, diferente pregunta. Me gustaría tener respuestas específicamente para el caso a) -d), para las cuales no puedo encontrar respuestas en la pregunta duplicada.


Sin embargo, me pregunto, ¿hay algún caso, cuando se usa == está perfectamente bien?

Claro que hay Una categoría de ejemplos son los usos que no implican cómputo, por ejemplo, establecedores que solo deben ejecutar cambios:

void setRange(float min, float max)
{
    if(min == m_fMin && max == m_fMax)
        return;

    m_fMin = min;
    m_fMax = max;

    // Do something with min and/or max
    emit rangeChanged(min, max);
}

Ver también ¿Es flotante == alguna vez está bien? y ¿Está el punto flotante == alguna vez OK? .


Asumiendo la semántica de IEEE 754, definitivamente hay algunos casos donde puedes hacer esto. Los cálculos de números de coma flotante convencionales son exactos siempre que pueden, que por ejemplo incluyen (pero no se limitan a) todas las operaciones básicas donde los operandos y los resultados son enteros.

Entonces, si sabes a ciencia cierta que no haces nada que resulte en algo irrepresentable, estás bien. Por ejemplo

float a = 1.0f;
float b = 1.0f;
float c = 2.0f;
assert(a + b == c); // you can safely expect this to succeed

La situación solo empeora si tiene cálculos con resultados que no son exactamente representables (o que involucran operaciones que no son exactas) y cambia el orden de las operaciones.

Tenga en cuenta que el estándar de C ++ en sí mismo no garantiza la semántica de IEEE 754, pero eso es lo que puede esperar tratar la mayor parte del tiempo.


En mi humilde opinión, no debe confiar en el operador == porque tiene muchos casos de esquina. El mayor problema es el redondeo y la precisión extendida. En el caso de x86, las operaciones de coma flotante se pueden realizar con mayor precisión de la que puede almacenar en variables (si utiliza coprocesadores, SSE operaciones SSE IIRC utilizan la misma precisión que el almacenamiento).

Esto generalmente es bueno, pero esto causa problemas como: 1./2 != 1./2 porque un valor es variable de forma y el segundo es de registro de coma flotante. En los casos más simples, funcionará, pero si agrega otras operaciones de coma flotante, el compilador podría decidir dividir algunas variables en la pila, cambiando sus valores, cambiando así el resultado de la comparación.

Para tener una certeza del 100%, necesita ver el ensamblaje y ver qué operaciones se realizaron antes en ambos valores. Incluso la orden puede cambiar el resultado en casos no triviales.

En general, ¿qué sentido tiene usar == ? Debe usar algoritmos que sean estables. Esto significa que funcionan incluso si los valores no son iguales, pero aún dan los mismos resultados. El único lugar donde sé que == podría ser útil es serializar / deserializar donde sabes exactamente qué resultado quieres y puedes modificar la serialización para archivar tu objetivo.


Los casos de riesgo pueden "funcionar". Los casos prácticos aún pueden fallar. Un problema adicional es que a menudo la optimización provocará pequeñas variaciones en la forma en que se realiza el cálculo de modo que simbólicamente los resultados sean iguales pero numéricamente sean diferentes. El ejemplo anterior podría, teóricamente, fallar en tal caso. Algunos compiladores ofrecen una opción para producir resultados más consistentes a un costo de rendimiento. Aconsejaría "siempre" evitar la igualdad de los números de coma flotante.

La igualdad de las mediciones físicas, así como los flotadores almacenados digitalmente, a menudo no tiene sentido. Entonces, si comparas si las carrozas son iguales en tu código, probablemente estés haciendo algo mal. Por lo general, desea mayor o menor que o dentro de una tolerancia. Con frecuencia, el código puede reescribirse para evitar estos tipos de problemas.





floating-point-comparison