2010-03-17 10 views
11

Considere el siguiente código:C# - inconsistente resultado de la operación matemática en 32 bits y de 64 bits

double v1 = double.MaxValue; 
double r = Math.Sqrt(v1 * v1); 

r = double.MaxValue en 32 bits máquina r = infinito en la máquina de 64 bits

Desarrollamos en una máquina de 32 bits y, por lo tanto, desconocemos el problema hasta que nos lo notifique el cliente. ¿Por qué ocurre tal inconsistencia? ¿Cómo prevenir que esto suceda?

+2

Entrega '+ inf' en máquinas de 32 y 64 bits aquí. – Joey

Respuesta

21

El conjunto de instrucciones x86 tiene problemas de consistencia de punto flotante complicado debido a la forma en que funciona la FPU. Los cálculos internos se realizan con bits más significativos que los que se pueden almacenar en un doble, lo que causa el truncamiento cuando el número se vacía desde la pila FPU a la memoria.

Eso se corrigió en el compilador x64 JIT, usa instrucciones SSE, los registros SSE tienen el mismo tamaño que un doble.

Esto lo va a pasar cuando sus cálculos prueban los límites de precisión y rango de coma flotante. Nunca querrá acercarse a necesitar más de 15 dígitos significativos, nunca querrá acercarse a 10E308 o 10E-308. Ciertamente, nunca desea cuadrar el mayor valor representable. Esto nunca es un problema real, los números que representan cantidades físicas no se acercan.

Aproveche esta oportunidad para averiguar cuál es el problema en sus cálculos. Es muy importante que ejecute el mismo sistema operativo y el mismo hardware que usa su cliente, a la vez que obtiene las máquinas necesarias para hacerlo. El código de envío que solo se prueba en la máquina x86 no se prueba.

La corrección D & D es Project + Properties, pestaña Compilar, Platform Target = x86.


Fwiw, el mal resultado en x86 es causado por un error en el compilador JIT.Se genera este código:

 double r = Math.Sqrt(v1 * v1); 
00000006 fld   dword ptr ds:[009D1578h] 
0000000c fsqrt    
0000000e fstp  qword ptr [ebp-8] 

La instrucción FMUL falta, eliminado por el optimizador de código en modo de lanzamiento. Sin duda se desencadenó al ver el valor en double.MaxValue. Es un error, puede informarlo en connect.microsoft.com. Sin embargo, estoy bastante seguro de que no lo arreglarán.

+0

"byte you" - juego de palabras intencional o error tipográfico? ;) –

+7

@Jonners: bastante intencional. –

1

El problema es que Math.Sqrt espera un doble como argumento. v1 * v1 no se puede almacenar como doble y se desborda dando como resultado un comportamiento indefinido .

+1

Sin comportamiento indefinido con IEEE 754. Un 'doble' desbordado se define como infinito positivo y las reglas sobre cómo hacer más operaciones con eso son bastante claras. – Joey

+0

@Johannes, gracias por esta aclaración. –

1

double.MaxValue * double.MaxValue es un desbordamiento.

Debe evitar que el cálculo se desborde en lugar de confiar en el comportamiento de 32 bits que informó (que, como se comentó, parece poco probable).

[Son los 32 y 64 bits se basa la misma configuración y ajustes?]

+0

Exactamente lo mismo. –

2

He intentado esto en x86 y x64 en la depuración y liberación modo:

x86 debug: Double.MaxValue 
x64 debug: Infinity 
x86 release: Infinity 
x64 release: Infinity 

Por lo tanto, parece que es sólo en modo de depuración que obtienes ese resultado.

No sé por qué hay una diferencia, sin embargo, el código x86 en modo de depuración:

  double r = Math.Sqrt(v1 * v1); 
00025bda fld   qword ptr [ebp-44h] 
00025bdd fmul  st,st(0) 
00025bdf fsqrt    
00025be1 fstp  qword ptr [ebp-5Ch] 
00025be4 fld   qword ptr [ebp-5Ch] 
00025be7 fstp  qword ptr [ebp-4Ch] 

es el mismo que el código en modo de lanzamiento:

  double r = Math.Sqrt(v1 * v1); 
00000027 fld   qword ptr [ebp-8] 
0000002a fmul  st,st(0) 
0000002c fsqrt    
0000002e fstp  qword ptr [ebp-18h] 
00000031 fld   qword ptr [ebp-18h] 
00000034 fstp  qword ptr [ebp-10h] 
+0

No está mirando el código optimizado JIT. Herramientas + Opciones, Depuración, desmarque "Suprimir optimización JIT". Verás que JITter no se molesta en generar la instrucción fmul. Eso es un error. –

+0

@nobugz: Lo intenté, pero el código sigue siendo el mismo. – Guffa

3

Este es un duplicado cercano de su

Why does this floating-point calculation give different results on different machines?

Mi respuesta a esa pregunta también responde a éste. En resumen: diferentes tipos de hardware pueden dar resultados más o menos precisos dependiendo de los detalles del hardware.

¿Cómo evitar que esto suceda? Como el problema está en el chip, tienes dos opciones. (1) No haga ninguna matemática en números de coma flotante. Haz todas tus matemáticas en enteros. La matemática entera es 100% consistente de un chip a otro. O (2) requieren que todos sus clientes usen el mismo hardware en el que se desarrollan.

Tenga en cuenta que si elige (2), es posible que todavía tenga problemas; pequeños detalles como si un programa fue compilado, depuración o venta al por menor puede cambiar si los cálculos de coma flotante se realizan con mayor precisión o no. Esto puede causar resultados inconsistentes entre compilaciones de depuración y venta minorista, que también es inesperado y confuso. Si su requerimiento de consistencia es más importante que su requerimiento de velocidad, entonces tendrá que implementar su propia biblioteca de coma flotante que haga todos sus cálculos en números enteros.

+0

Desafortunadamente, necesitamos que el programa se ejecute como puro de 64 bits en Windows de 64 bits. Es una aplicación de ingeniería donde hacer todas las matemáticas en números enteros es poco práctico. –

Cuestiones relacionadas