Comparación entre reales

4387 vistas

Cuando comparamos 2 número de coma flotante con el operador =, podemos encontrarnos con muchas sorpresas: 2 números a priori iguales puede que no lo sean. Esto es por culpa de la representación del coma flotante, el cual es una aproximación del número y no una representación exacta. Por ejemplo:



delphi
  1. var
  2.   a: double;
  3. begin
  4.   a := 11 + 1.11;
  5.   if (a = 12.11) then
  6.     showMessage('igual')
  7.   else
  8.     showMessage('no igual') // <-- mostrará este mensaje
  9. end;



Por suerte, a partir de Delphi 6, Borland nos provee de 3 funciones en la unidad Math:



delphi
  1. // indica si 2 valores en coma flotante son (aproximádamente) iguales.
  2. function SameValue(const A, B: Single; Epsilon: Single = 0): Boolean; overload;
  3.  
  4. // Las 2 siguientes usan SameValue :
  5.  
  6. // Indica si una variable o una expresión en coma flotante es 0 o un valor próximo a 0.
  7. function IsZero(const A: Single; Epsilon: Single = 0): Boolean; overload;
  8. // Devuelve la relación entre dos valores numéricos.
  9. function CompareValue(const A, B: Single; Epsilon: Single = 0): TValueRelationship; overload;



Estas tres funciones están definidas para los tipos Single, Double y Extended. CompareValue también lo está para Integer y Int64.

Por lo tanto, el código anterior que no funcionaba quedará asÃ:



delphi
  1. uses Math;
  2. var
  3.   a : double;
  4. begin
  5.   a := 11 + 1.11;
  6.   if SameValue(a, 12.11) then
  7.     ShowMessage('igual')      // <-- mostrará este mensaje
  8.   else
  9.     ShowMessage('no igual');
  10. end;



La ayuda de Delphi nos indica que Epsilon es la cantidad máxima en que A y B pueden diferir para considerarse iguales. Por defecto este valor es 0, pero mirando el código, constatamos que si Epsilon es 0 (cosa que no tendrÃa sentido), el valor es corregido.

Si tenemos una versión anterior a Delphi 6
Tendremos que escribir unas funciones que haga este trabajo



delphi
  1. function RealesIguales(n1,n2 : extended) : boolean;
  2. begin
  3.   Result := Abs(n1 - n2) < Min(abs(n1), abs(n2)) * 1E-16;
  4. end;
  5.  
  6. function RealesIguales(n1,n2 : double) : boolean;
  7. begin
  8.   Result := Abs(n1 - n2) < Min(abs(n1), abs(n2)) * 1E-12;
  9. end;
  10.  
  11. function RealesIguales(n1,n2 : single) : boolean;
  12. begin
  13.   Result := Abs(n1 - n2) < Min(abs(n1), abs(n2)) * 1E-4;
  14. end;