c# - programacion - punto flotante normalizado




¿La aritmética de punto flotante es estable? (4)

Esta pregunta ya tiene una respuesta aquí:

Sé que los números de coma flotante tienen precisión y los dígitos después de la precisión no son confiables.

Pero, ¿y si la ecuación utilizada para calcular el número es la misma? ¿Puedo suponer que el resultado sería el mismo también?

por ejemplo, tenemos dos números flotantes x e y . ¿Podemos suponer que el resultado x/y de la máquina 1 es exactamente el mismo que el resultado de la máquina 2? IE == comparación sería verdadera


Pero, ¿y si la ecuación utilizada para calcular el número es la misma? ¿Puedo suponer que el resultado sería el mismo también?

No, no necesariamente

En particular, en algunas situaciones el JIT puede usar una representación intermedia más precisa, por ejemplo, 80 bits cuando los datos originales son 64 bits, mientras que en otras situaciones no lo hará. Eso puede dar como resultado ver resultados diferentes cuando cualquiera de los siguientes es verdadero:

  • Tiene un código ligeramente diferente, por ejemplo, utiliza una variable local en lugar de un campo, que puede cambiar si el valor se almacena en un registro o no. (Ese es un ejemplo relativamente obvio, hay otros mucho más sutiles que pueden afectar las cosas, como la existencia de un bloqueo de try en el método ...)
  • Estás ejecutando en un procesador diferente (yo solía observar las diferencias entre AMD e Intel, también puede haber diferencias entre diferentes CPU del mismo fabricante)
  • Está ejecutando con diferentes niveles de optimización (por ejemplo, bajo un depurador o no)

De la especificación C # 5 sección 4.1.6:

Las operaciones de punto flotante se pueden realizar con mayor precisión que el tipo de resultado de la operación. Por ejemplo, algunas arquitecturas de hardware admiten un tipo de punto flotante "extendido" o "largo doble" con mayor rango y precisión que el tipo doble, e implícitamente realizan todas las operaciones de coma flotante con este tipo de precisión superior. Solo a un costo excesivo en rendimiento pueden tales arquitecturas de hardware realizar operaciones de punto flotante con menos precisión, y en lugar de requerir una implementación que pierda rendimiento y precisión, C # permite que se use un tipo de mayor precisión para todas las operaciones de coma flotante . Además de ofrecer resultados más precisos, esto rara vez tiene efectos mensurables. Sin embargo, en las expresiones de la forma x * y / z , donde la multiplicación produce un resultado que está fuera del rango doble, pero la división posterior trae el resultado temporal nuevamente al doble rango, el hecho de que la expresión se evalúa en un nivel superior el formato de rango puede causar que se produzca un resultado finito en lugar de un infinito.


Además de las otras respuestas, es posible x/y != x/y incluso en la misma máquina .

Los cálculos de coma flotante en x86 se realizan usando registros de 80 bits, pero se truncan a 64 bits cuando se almacenan. De modo que es posible calcular la primera división, truncarla, volver a cargarla en la memoria y compararla con la división no truncada.

Consulte here para obtener más información (es un enlace de C ++, pero el razonamiento es el mismo)


Francamente, no esperaría que dos lugares en la misma base de código devuelvan lo mismo para x/y para la misma y - en el mismo proceso en la misma máquina; puede depender de cómo se optimizan exactamente x con el compilador / JIT; si se registran de forma diferente, pueden tener diferentes precisiones intermedias. Se realizan muchas operaciones utilizando más bits de los que espera (tamaño de registro); y exactamente cuando esto se reduce a 64 bits puede afectar el resultado. La elección de ese "exactamente cuándo" puede depender de qué más está sucediendo en el código circundante.


La respuesta de Jon es, por supuesto, correcta. Sin embargo, ninguna de las respuestas ha explicado cómo puede asegurarse de que la aritmética de coma flotante se realice con la precisión garantizada por la especificación y no más .

C # trunca automáticamente cualquier flotante a su representación canónica de 32 o 64 bits en las siguientes circunstancias:

  • Pones un elenco explícito redundante: x + y podría tener xey como números de mayor precisión que luego se agregan. Pero (double)((double)x+(double)y) asegura que todo se trunca a una precisión de 64 bits antes y después de que ocurra la matemática.
  • Cualquier almacenamiento en un campo de instancia de una clase , campo estático, elemento de matriz o puntero desreferenciado siempre se trunca. (Las tiendas a los locales, los parámetros y los temporales no se pueden truncar, se pueden registrar. Los campos de una estructura pueden estar en el grupo a corto plazo, que también se puede registrar).

Estas garantías no están hechas por la especificación del lenguaje, pero las implementaciones deben respetar estas reglas. Las implementaciones de Microsoft de C # y CLR lo hacen.

Es un dolor escribir el código para asegurar que la aritmética de punto flotante sea predecible en C #, pero se puede hacer. Tenga en cuenta que hacerlo probablemente ralentizará su aritmética.

Las quejas sobre esta horrible situación deben dirigirse a Intel, no a Microsoft; ellos son los que diseñaron chips que hacen que la aritmética predecible sea más lenta.

Además, tenga en cuenta que esta es una pregunta frecuente. Puede considerar cerrar esto como un duplicado de:

¿Por qué difiere la precisión de coma flotante en C # cuando está separada por parantheses y cuando está separada por declaraciones?

¿Por qué este cálculo de punto flotante da resultados diferentes en diferentes máquinas?

Lanzar un resultado para flotar en el método que devuelve el resultado de los cambios de flotación

(.1f + .2f ==. 3f)! = (.1f + .2f) .Equals (.3f) ¿Por qué?

¿Coaccionar al punto flotante para que sea determinista en .NET?

C # XNA Visual Studio: ¿Diferencia entre los modos "liberar" y "depurar"?

C # - Resultado de operación matemática inconsistente en 32 bits y 64 bits

Error de redondeo en C #: Diferentes resultados en diferentes PC

Comportamiento extraño del compilador con literales float versus variables float







floating-point