Precisión de punto flotante al pasar de i386 a x86_64

Tengo una aplicación que fue desarrollada para Linux x86 32 bits. Hay muchas operaciones de punto flotante y muchas pruebas según los resultados. Ahora lo estamos portando a x86_64, pero los resultados de las pruebas son diferentes en esta architecture. No queremos mantener un conjunto separado de resultados para cada architecture.

Según el artículo Introducción a GCC: para los comstackdores GNU gcc y g ++, el problema es que GCC en X86_64 asume fpmath = sse mientras que x86 asume fpmath = 387 . El 387 FPU usa una precisión interna de 80 bits para todas las operaciones y solo convierte el resultado a un tipo de punto flotante dado (flotante, doble o largo doble), mientras que SSE usa el tipo de los operandos para determinar su precisión interna.

Puedo forzar -mfpmath = 387 cuando compilo mi propio código y todas mis operaciones funcionan correctamente, pero cada vez que llamo a alguna función de biblioteca (sin, cos, atan2, etc.) los resultados vuelven a ser incorrectos. Supongo que se debe a que se compiló libm sin la anulación fpmath.

Intenté construir libm (glibc) con la emulación 387, pero causó muchos lockings (no sé si hice algo mal).

¿Hay una manera de forzar a todo el código en un proceso a usar la emulación 387 en x86_64? ¿O tal vez alguna biblioteca que devuelve los mismos valores que libm en ambas architectures? ¿Alguna sugerencia?

Con respecto a la pregunta “¿Necesita la precisión de 80 bits”, debo decir que esto no es un problema para una operación individual? En este caso simple, la diferencia es realmente pequeña y no hace ninguna diferencia. Sin embargo, cuando se combinan muchas operaciones, el error se propaga y la diferencia en el resultado final ya no es tan pequeña y marca la diferencia. Así que supongo que necesito la precisión de 80 bits.

Yo diría que necesitas arreglar tus pruebas. En general, se está configurando para la decepción si asume que las matemáticas de punto flotante son precisas. En lugar de probar la igualdad exacta , compruebe si está lo suficientemente cerca del resultado esperado. Lo que ha encontrado no es un error, después de todo, por lo que si sus pruebas informan errores, las pruebas son incorrectas. 😉

Como ya descubrió, todas las bibliotecas en las que confía asumirán un punto flotante de SSE, por lo que, a menos que planee comstackr todo manualmente, ahora y siempre, solo para poder establecer el modo FP en x87, será mejor que lo haga. con el problema ahora, y solo aceptar que FP Math no es 100% precisa, y en general no dará el mismo resultado en dos plataformas diferentes. (Creo que la CPU de AMD también produce resultados ligeramente diferentes en matemáticas x87).

¿ Necesita absolutamente precisión de 80 bits? (Si es así, obviamente no hay muchas alternativas, aparte de comstackr todo para usar el FP de 80 bits).

De lo contrario, ajuste sus pruebas para realizar comparaciones y pruebas de igualdad dentro de una pequeña epsilon. Si la diferencia es menor que épsilon, los valores se consideran iguales.

La precisión de 80 bits es realmente peligrosa. El problema es que en realidad se conserva siempre que la variable se almacene en el registro de la CPU. Cuando se fuerza a la RAM, se trunca a la precisión de tipo. Por lo tanto, puede hacer que una variable realmente cambie su valor aunque no le haya pasado nada en el código.

Si desea long double precisión long double , use la long double para todas sus variables de punto flotante, en lugar de esperar que la float o el double tengan una precisión mágica adicional. Esto es realmente una obviedad.

El punto flotante SSE y el punto flotante 387 usan instrucciones completamente diferentes, por lo que no hay manera de convencer a las instrucciones fp de SSE para que utilicen el 387. Probablemente la mejor manera de lidiar con esto es renunciar a su conjunto de pruebas para obtener resultados ligeramente diferentes, y no depender de los resultados son los mismos en el último bit.