2010-05-02 24 views
10

Si tengo las variables a, b, una c del tipo double, c: = a/b, y doy valores a y b de 7 y 10, entonces el valor de c de 0.7 registra MENOS DE 0.70.Delphi Math: ¿Por qué 0.7 <0.70?

Por otro lado, si las variables son todas de tipo extendido, entonces el valor de c de 0.7 no se registra como menor que 0,70.

Esto parece extraño. ¿Qué información me estoy perdiendo?

+0

posible duplicado de http://stackoverflow.com/questions/1661273/java-floating-point-arithmetic –

+10

Esto es una de las preguntas más comunes (¿la más común?) sobre SO, y no tiene nada en particular que ver con Delphi, pero con la forma en que los comprters almacenan los números de coma flotante. –

+1

No es eso lo que impide que se vote :) –

Respuesta

11

No hay representación para el número matemático 0.7 en coma flotante binario. Su extracto calcula en c el double más cercano, que (según lo que dice, no lo he marcado) está un poco por debajo de 0.7.

Aparentemente en precisión extendida, el número de coma flotante más cercano a 0.7 está un poco por encima. Pero todavía no hay una representación exacta para 0.7. No hay ninguna con precisión en el punto flotante binario.

Como regla general, cualquier número no entero cuyo último decimal distinto de cero no es 5 no se puede representar exactamente como un número binario de coma flotante (el inverso no es verdadero: 0,05 tampoco se puede representar exactamente).

5

Te has perdido This Thing.

Consulte especialmente el capítulo 'Accuracy problems'. Ver también la respuesta de Pascal. Para reparar su código sin utilizar el tipo Extended, debe agregar la unidad Math y usar desde allí la función SameValue especialmente diseñada para este propósito.

Asegúrese de utilizar un valor Epsilon diferente de 0 cuando utiliza SameValue en su caso.

Por ejemplo:

var 
    a, b, c: double; 


begin 
    a:=7; b:=10; 
    c:=a/b; 

    if SameValue(c, 0.70, 0.001) then 
    ShowMessage('Ok') 
    else 
    ShowMessage('Wrong!'); 
end; 

HTH

18

En primer lugar, hay que señalar que los literales de flotar en Delphi son del tipo extendido. Entonces, cuando se compara un doble con un literal, el doble probablemente primero se "expande" a Extendida, y luego se compara. (Editar: Esto es cierto solo en la aplicación de 32 bits. En la aplicación de 64 bits, Extended es un alias de Double)

Aquí, se mostrará todo ShowMessage.

procedure DoSomething; 
var 
    A, B : Double; 
begin 
    A := 7/10; 
    B := 0.7; //Here, we lower the precision of "0.7" to double 

    //Here, A is expanded to Extended... But it has already lost precision. This is (kind of) similar to doing Round(0.7) <> 0.7 
    if A <> 0.7 then 
    ShowMessage('Weird'); 

    if A = B then //Here it would work correctly. 
    ShowMessage('Ok...'); 

    //Still... the best way to go... 
    if SameValue(A, 0.7, 0.0001) then 
    ShowMessage('That will never fails you'); 
end; 

Aquí un poco de literatura para usted

What Every Computer Scientist Should Know About Floating-Point Arithmetic

+3

+1 para _el_ enlace canónico con respecto a la aritmética de coma flotante. –

+1

+1 para el enlace, y menciona el extendido frente al doble. –

+0

Amigo. :-) Usted es maravilloso. –

8

Tiene que ver con el número de dígitos de precisión en los dos tipos diferentes de coma flotante que está utilizando, y el hecho de que muchos de números no se puede representar exactamente, independientemente de la precisión. (Del lado puramente matemático: los números irracionales superan en número a los racionales)

Tome 2/3, por ejemplo. No se puede representar exactamente en decimal. Con 4 dígitos significativos, se representaría como 0.6667. Con 8 dígitos significativos, sería 0.66666667. El 7 final es un rodeo que refleja que el siguiente dígito sería> 5 si hubiera espacio para guardarlo.

0.6667 es mayor que 0.66666667, por lo que la computadora evaluará 2/3 (4 dígitos)> 2/3 (8 dígitos).

Lo mismo ocurre con su .7 vs .70 en vars dobles y extendidos.

Para evitar este problema específico, intente utilizar el mismo tipo numérico en todo su código. Cuando se trabaja con números flotantes en general, hay muchas cosas pequeñas de las que hay que cuidarse. Lo más importante es no escribir el código para comparar dos carrozas para la igualdad, incluso si deben tener el mismo valor, existen muchos factores en los cálculos que pueden hacer que terminen siendo un poco diferentes. En lugar de comparar por igualdad, debe probar que la diferencia entre los dos números es muy pequeña. Cuán pequeña debe ser la diferencia depende de usted y de la naturaleza de sus cálculos, y generalmente se la conoce como épsilon, tomada del teorema del cálculo y la prueba.

+0

En realidad, todos los números computables se pueden representar exactamente :) – fishlips

+1

Creo que esta es una explicación razonable, pero agregaría que "muchos números que tienen una representación decimal de terminación racional no tienen una representación binaria de coma flotante de terminación precisa (racional) ". Ese hecho parece escapar de una gran cantidad de aviso del desarrollador. –