Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare double in delphi?

Tags:

types

delphi

We are facing issue with data type double comparison:

if(p > pmax) then begin   Showmessage(''); end 

If both values are 100 (p=100 and pmax = 100), then also it is going inside if clause.

like image 479
SSE Avatar asked May 24 '11 05:05

SSE


2 Answers

The Math.pas unit includes functions such as SameValue(), IsZero(), CompareValue() which handle floating type comparisons and equality.

const   EPSILON = 0.0000001; begin       if CompareValue(p, pMax, EPSILON) = GreaterThanValue then     ShowMessage('p greater than pMax'); 

The constant GreaterThanValue is defined in Types.pas

If you're comparing very large values you shouldn't use a constant for epsilon, instead your epsilon value should be calculated based on the values you're comparing.

var   epsilon: double; begin       epsilon := Max(Min(Abs(p), Abs(pMax)) * 0.000001, 0.000001);   if CompareValue(p, pMax, epsilon) = GreaterThanValue then     ShowMessage('p greater than pMax'); 

Note that if you use CompareValue(a, b, 0) or in XE2 and later CompareValue(a, b), Delphi will automatically fill in a good epsilon for you.

From the Delphi Math unit:

function SameValue(const A, B: Extended; Epsilon: Extended): Boolean; begin   if Epsilon = 0 then     Epsilon := Max(Min(Abs(A), Abs(B)) * ExtendedResolution, ExtendedResolution);   if A > B then     Result := (A - B) <= Epsilon   else     Result := (B - A) <= Epsilon; end; 

As of Delphi XE2 there are now overloads for all these functions that do not require an epsilon parameter and instead calculate one for you (similar to passing a 0 value for epsilon). For code clarity I would recommend calling these simpler functions and let Delphi handle the epsilon.

The only reason not to use the overloads without epsilon parameters would be when performance is crucial and you want to avoid the overhead of having the epsilon repeatedly calculated.

like image 89
LachlanG Avatar answered Sep 23 '22 00:09

LachlanG


There are several problems with comparing Doubles. One problem is that what you see is not exactly what you get due to rounding. You can have 99.999999996423 and 100.00000000001632, which are both rounded to 100, but they are not equal.

The solution is to use a margin so that, if the difference of the two Doubles lies within the margin, you accept them as equal.

You can create an IsEqual function using the margin as an optional parameter:

function IsEqual(const ANumber1, ANumber2: Double; const AMargin: Double = cMargin): Boolean; begin   Result := Abs(ANumber1-ANumber2) <= AMargin; end; 
like image 25
Toon Krijthe Avatar answered Sep 25 '22 00:09

Toon Krijthe