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

¿Por qué este código imprime False en .NET 4? Parece que algún comportamiento inesperado está siendo causado por el lanzamiento explícito.

Me gustaría obtener una respuesta más allá de que “el punto flotante es inexacto” o “no hagas eso”.

 float a(float x, float y) { return ( x * y ); } float b(float x, float y) { return (float)( x * y ); } void Main() { Console.WriteLine( a( 10f, 1f/10f ) == b( 10f, 1f/10f ) ); } 

PD: Este código vino de una prueba de unidad, no de código de versión. El código fue escrito de esta manera deliberadamente. Sospeché que finalmente fallaría, pero quería saber exactamente cuándo y por qué. La respuesta demuestra la validez de esta técnica porque proporciona una comprensión que va más allá de la comprensión habitual del determinismo de punto flotante. Y ese era el punto de escribir este código de esta manera; exploración deliberada.

PPS: la prueba de unidad pasaba en .NET 3.5, pero ahora falla después de la actualización a .NET 4.

El comentario de David es correcto pero insuficientemente fuerte. No hay garantía de que hacer ese cálculo dos veces en el mismo progtwig produzca los mismos resultados.

La especificación C # es extremadamente clara en este punto:


Las operaciones de punto flotante se pueden realizar con mayor precisión que el tipo de resultado de la operación. Por ejemplo, algunas architectures 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 architectures 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.


El comstackdor de C #, el jitter y el tiempo de ejecución tienen una gran amplitud para proporcionarle resultados más precisos que los requeridos por la especificación, en cualquier momento, por capricho; no es obligatorio que lo hagan de manera consistente y de hecho lo hacen no.

Si no te gusta, entonces no uses números de coma flotante binarios; usar decimales o racionales de precisión arbitraria.

No entiendo por qué fundir para flotar en un método que devuelve flotar hace la diferencia que hace

Excelente punto

Su progtwig de muestra demuestra cómo pequeños cambios pueden causar grandes efectos. Tenga en cuenta que en alguna versión del tiempo de ejecución, la conversión a flotación explícitamente da un resultado diferente que no hacerlo. Cuando expulsa explícitamente a flotación, el comstackdor de C # da una pista al tiempo de ejecución para decir “saque esto del modo de muy alta precisión si está utilizando esta optimización”. Como se especifica en la especificación, esto tiene un costo de rendimiento potencial.

Que hacerlo llegue a la “respuesta correcta” es meramente un feliz accidente; se obtiene la respuesta correcta porque en este caso perder precisión pasó a perderla en la dirección correcta .

¿Cómo es .net 4 diferente?

Usted pregunta cuál es la diferencia entre 3.5 y 4.0 tiempos de ejecución; la diferencia es claramente que en 4.0, el jitter elige ir a una mayor precisión en su caso particular, y el jitter 3.5 elige no hacerlo. Eso no quiere decir que esta situación sea imposible en 3.5; Ha sido posible en todas las versiones del tiempo de ejecución y en todas las versiones del comstackdor de C #. Acabas de encontrar un caso donde, en tu máquina, difieren en sus detalles. Pero el jitter siempre se ha permitido para hacer esta optimización, y siempre lo ha hecho a su antojo.

El comstackdor de C # también tiene derecho a elegir realizar optimizaciones similares al calcular flotantes constantes en tiempo de comstackción. Dos cálculos aparentemente idénticos en constantes pueden tener diferentes resultados dependiendo de los detalles del estado de tiempo de ejecución del comstackdor.

En términos más generales, su expectativa de que los números de punto flotante deben tener las propiedades algebraicas de los números reales está completamente fuera de línea con la realidad; ellos no tienen esas propiedades algebraicas. Las operaciones de coma flotante ni siquiera son asociativas ; ciertamente no obedecen las leyes de los inversos multiplicativos, como parece esperar que lo hagan. Los números de coma flotante son solo una aproximación de la aritmética real; una aproximación lo suficientemente cercana para, por ejemplo, simular un sistema físico o calcular estadísticas de resumen, o algo por el estilo.

No tengo comstackdor de Microsoft en este momento y Mono no tiene ese efecto. Por lo que sé, GCC 4.3+ usa gmp y mpfr para calcular algunas cosas en tiempo de comstackción . El comstackdor de C # puede hacer lo mismo para métodos no virtuales, estáticos o privados en el mismo ensamblado. El lanzamiento explícito puede interferir con dicha optimización (pero no veo ninguna razón por la cual no pueda tener el mismo comportamiento). Es decir, se puede alinear con el cálculo de la expresión constante a algún nivel (para b() puede ser, por ejemplo, hasta el modelo).

GCC también tiene la optimización que promueve la operación con mayor precisión si tiene sentido.

Entonces consideraría tanto la optimización como la razón potencial. Pero para ambos no veo ninguna razón por la cual hacer un casting explícito del resultado pueda tener algún significado adicional como “estar más cerca del estándar”.