punto programacion normalizado normalizada mostrar mantisa informatica flotante float ejemplos decimales c# floating-point

c# - programacion - ¿La aritmética de punto flotante es estable?



punto flotante informatica (6)

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.

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


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)


En teoría, en un sistema conforme IEEE 754, se supone que la misma operación con los mismos valores de entrada produce el mismo resultado.

Como Wikipedia lo resume:

El IEEE 754-1985 permitió muchas variaciones en las implementaciones (como la codificación de algunos valores y la detección de ciertas excepciones). IEEE 754-2008 ha fortalecido muchos de estos, pero aún quedan algunas variaciones (especialmente para formatos binarios). La cláusula de reproducibilidad recomienda que los estándares de lenguaje proporcionen un medio para escribir programas reproducibles (es decir, programas que producirán el mismo resultado en todas las implementaciones de un idioma), y describe lo que se debe hacer para lograr resultados reproducibles.

Como de costumbre, sin embargo, la teoría es diferente de la práctica. La mayoría de los lenguajes de programación de uso común, incluido C #, no se ajustan estrictamente a IEEE 754, y no necesariamente proporcionan un medio para escribir un programa reproducible.

Además, las CPU / FPU modernas hacen que resulte un poco incómodo garantizar el cumplimiento estricto de IEEE 754. Por defecto funcionarán con "precisión extendida", almacenando valores con más bits que un doble internamente. Si desea una semántica estricta, debe extraer valores de la FPU en un registro de la CPU, verificar y manejar varias excepciones FPU, y luego volver a introducir valores entre cada operación de la FPU. Debido a esta incomodidad, la conformidad estricta tiene una penalización de rendimiento, incluso a nivel de hardware. El estándar C # eligió un requisito más "descuidado" para evitar imponer una penalización de rendimiento en el caso más común donde las pequeñas variaciones no son un problema.

Nada de esto es a menudo un problema en la práctica, ya que la mayoría de los programadores han internalizado la idea (incorrecta, o al menos engañosa) de que las matemáticas de coma flotante son inexactas. Además, los errores de los que estamos hablando aquí son extremadamente pequeños, lo suficiente como para que sean eclipsados ​​por la pérdida de precisión mucho más común causada por la conversión de decimal.


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


No, no lo hace. El resultado del cálculo puede diferir por CPU, ya que la implementación de la aritmética de coma flotante puede diferir según el fabricante de la CPU o el diseño de la CPU. Incluso recuerdo un error en la aritmética de punto flotante en algunos procesadores de Intel, que arruinó nuestros cálculos.

Y luego hay una diferencia en cómo se evalúa el código en el compilador JIT.