ADUG
Home
About Us
Services
Meetings
Fees
Mailing List
Rules
Reference Papers
Downloads
Apply to Join
Delphi Jobs
Special Offers
Maths Corner

 

by Glenn Crouch

 

Rounding Errors

When using floating-point data types such as Single, Double or Extended you must be careful of what happens with rounding errors. By way of example, let’s assume we are working with a Calculator that can only display 4 decimal digits:

As you can see rather than obtain 1 we instead have a number that is close to 1. This problem gets even more involved when we start to allow for binary - just as a Third cannot be expressed in a finite number of places using Base 10 (this is what causes the rounding error above) - neither can a Tenth be expressed in a finite number of places using Base 2 - and this is the case for the IEEE standard format used for Single, Double and Extended.

Hence Equality is something we should avoid when using floating-point numbers, as something like the following:

var
  
X, Y: Double;

begin
...
    repeat

. . .

    until X = Y
;

        

may never terminate.

So we must always avoid comparing two floating-point values in equality and inequality (since <> is just the "opposite" of  =).

One way I like to handle this is with a Tolerance parameter. I have a unit called ESBGlobals.pas that stores all my global data types and global variables/constants that I want. In this I have:

var
    ESBTolerance = 0.00000001;

Of course you could put the declaration into your own maths unit with the routines below.

Then we can develop routines like the following:

function FloatIsZero (const X: Extended): Boolean;
// Is X = 0?
begin
    Result := abs (X) < ESBTolerance;
end;

function SameFloat (const X, Y: Extended): Boolean;
// Is X = Y?
begin
    Result := abs (X – Y) < ESBTolerance;
end;

Whilst the comparisons of  >  and  <  then are not as susceptible to problems, you can still encounter problems depending on how you are using them and hence we can use Tolerance here as well.

function GreaterFloat (const X, Y: Extended): Boolean;
// Is X > Y?
begin
Result := X – Y > ESBTolerance;
end;

function LesserFloat (const X, Y: Extended): Boolean;
// Is X < Y?
begin
Result := Y – X > ESBTolerance;
end;

You can even let the user “adjust” the Tolerance within your application.

The above routines are not good when you have very small numbers, unless you adjust the tolerance accordingly. Likewise for very large numbers you could use a Tolerance that is quite large. For example when dealing with numbers greater than 1,000,000,000 you could use a tolerance of 100.

Remember: Delphi's TDataTime is really just a Double, so all Date/Time calculations can suffer from the above.

In practical situations, you need to judge the accuracy of the data being used and set the Tolerance accordingly.

 

Maths Corner Home

 Copyright © 2001 Australian Delphi User Group and respective copyright owners.
All Rights Reserved | Disclaimer