2009-04-29 21 views
7

La pregunta es, ¿por qué estos fragmentos de código dan resultados diferentes?cálculo simple, diferentes resultados en C# y delphi

private void InitializeOther() 
{ 
    double d1, d2, d3; 
    int i1; 

    d1 = 4.271343859532459e+18; 
    d2 = 4621333065.0; 
    i1 = 5; 

    d3 = (i1 * d1) - Utils.Sqr(d2); 
    MessageBox.Show(d3.ToString()); 
} 

y

procedure TForm1.InitializeOther; 
var d1, d2, d3 : Double; 
    i1 : Integer; 
begin 
    d1:=4.271343859532459e+18; 
    d2:=4621333065.0; 
    i1:=5; 

    d3:=i1*d1-Sqr(d2); 
    ShowMessage(FloatToStr(d3)); 
end; 

El código de Delphi me da 816, mientras que el código C# me da 0. Con una calculadora, consigo 775. Puede alguien por favor, dame una explicación detallada?

¡Muchas gracias!

+1

error en Utils.Sqr quizás? –

+5

Este es un "cálculo simple"? No con números de coma flotante, no lo es. –

+3

Si cambia el tipo de Delphi de doble a extendido, la respuesta es 776, lo que demuestra el punto acerca del redondeo. Es posible que desee consultar una biblioteca bignum u otras bibliotecas científicas si va a tratar con números fp tan grandes. –

Respuesta

11

Delphi almacena los valores intermedios como Extendido (un tipo de coma flotante de 80 bits).Esta expresión es Extendida:

i1*d1-Sqr(d2); 

Lo mismo puede no ser cierto para C# (No lo sé). La precisión extra podría marcar la diferencia.

+4

Eso es exactamente lo que está pasando. De acuerdo con el archivo de ayuda Delphi, el framework .NET solo puede alcanzar una precisión doble. Delphi usa extendido por defecto. –

+0

Gracias, eso explica la diferencia en el comportamiento. –

+0

Tenga cuidado también con cálculos como este cuando se trata de la precisión 8087. He tenido dos instancias de diferentes valores entre la consola y las aplicaciones GUI desde el mismo código. En alguna parte, la palabra de control 8087 estaba siendo modificada. Si tiene dudas, y es importante, configúrelo explícitamente. FWIW. –

10

Tenga en cuenta que estás en los límites de la precisión de los datos de tipo doble aquí, lo que significa que los cálculos aquí no será precisa.

Ejemplo:

d1 = 4.271343859532459e+18 

que puede decirse que es lo mismo que:

d1 = 4271343859532459000 

y así:

d1 * i1 = 21356719297662295000 

en realidad, el valor en .NET será más como este:

2.1356719297662296E+19 

Tenga en cuenta el redondeo allí. Por lo tanto, en este nivel, no estás obteniendo las respuestas correctas.

0

Creo que este es un error causado por la precisión limitada (Sobre todo, porque usa dobles en lugar de números enteros). Tal vez d1 no es lo mismo después de la asignación. d2 * d2 seguramente será diferente al valor correcto ya que es más grande que 2^32.

Como 5 * d1 es incluso mayor que 2^64, incluso el uso de enteros de 64 bits no ayudará. Tendría que usar bignums o una clase entera de 128 bits para obtener el resultado correcto.

+0

Los cálculos se realizarán en dominio de punto flotante, el rango de tipos enteros ni siquiera entrará en juego aquí. – mghie

+0

Lo sé, solo quería aclarar que incluso usar números enteros no ayudará – schnaader

4

Una C# doble tiene como máximo 16 dígitos de precisión. Tomar 4.271343859532459e + 18 y multiplicar por 5 dará un número de 19 dígitos. Desea tener un número con solo 3 dígitos como resultado. El doble no puede hacer esto.

En C#, el tipo Decimal puede manejar este ejemplo, si sabe utilizar el formato 123M para inicializar los valores Decimal.

Decimal d1, d2, d3; 
    int i1; 
    d1 = 4.271343859532459e+18M; 
    d2 = 4621333065.0M; 
    i1 = 5; 
    d3 = (i1 * d1) - (d2*d2); 

    MessageBox.Show(d3.ToString()); 

Esto da 775.00 cual es la respuesta correcta.

+1

Tenga en cuenta que aunque el decimal tiene mucha mejor precisión (y usa más memoria) que el doble, también tiene un rango mucho más bajo (± 1.0 × 10^- 28 a ± 7.9 × 10^28). – Brian

+0

@Brian gracias por arreglar las inicializaciones Decimal. No los uso con la frecuencia suficiente para conocer la sintaxis xxxM. –

+0

¡Gracias por señalarme esto! Los decimales pueden ser la respuesta en mi situación, aunque el rango mucho más estrecho y la posible sobrecarga de rendimiento me hacen pensar dos veces antes de usarlos. Como esto es más bien un caso de esquina en mi aplicación, es posible que pueda factorizar los dobles de entrada para evitar que ocurra este tipo de cálculo. –

0

Básicamente, como han señalado otras personas, la precisión doble no es lo suficientemente precisa para la escala del cálculo que estás tratando de hacer. Delphi usa "precisión extendida" de manera predeterminada, que agrega otros 16 bits a Double para permitir un cálculo más preciso. .NET Framework no tiene un tipo de datos de precisión extendida.

No estoy seguro de qué tipo está usando su calculadora, pero aparentemente está haciendo algo diferente de Delphi y C#.

0

Según lo comentado por los otros, el doble no es lo suficientemente preciso para su cálculo. El decimal es una buena alternativa, aunque si alguien señaló que sería redondeado, no lo es.

En C#, el tipo decimal no pueden manejar este ejemplo fácilmente ya sea desde 4.271343859532459e + 18 se redondeará a 4271343859532460000.

Este no es el caso. La respuesta si usa decimal será correcta. Pero como dijo, el alcance es diferente.

Cuestiones relacionadas