Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ABS function failing in Delphi

Tags:

delphi

In Delphi6 or in Delphi 2010, declare two variables of type Currency (vtemp1,vtemp2) and feed them a value of 0.09. Embed one of the variables with the ABS function and compare it to the other. You would expect for the comparison to yield a positive result as the compiler watch reveals the same value for abs(vtemp1) and vtemp2. Oddly the if statement fails!!!

Notes: -This problem is experienced only when dealing with the number 0.09 (trying several other near values revealed normal results) -Declaring the variable as Double instead of currency, the problem ceases to exist.

like image 644
Johny Avatar asked Aug 13 '12 06:08

Johny


People also ask

What is ABS in Delphi?

Description. The Abs function returns the absolute value of a negative or positive number. It does this by removing a negative sign, if found. The Number can be any numeric type, and can even be a Variant, as long as it can be converted to a number.

What is the function of ABS ()?

Returns the absolute value of a number. The absolute value of a number is the number without its sign.


2 Answers

I think that the reason is type conversions. Abs() function returns real results, so currency variable casts to real. Take a look at documentation:

Currency is a fixed-point data type that minimizes rounding errors in monetary calculations. On the Win32 platform, it is stored as a scaled 64-bit integer with the four last significant digits implicitly representing decimal places. When mixed with other real types in assignments and expressions, Currency values are automatically divided or multiplied by 10000.

so Currency is fixed and real is floating-point. Sample code for your question is :

program Project3;
{$APPTYPE CONSOLE}

const VALUE = 0.09;
var a,b  : currency;
begin
    a := VALUE;
    b := VALUE;

    if a = Abs(b) then writeln('equal')
    else writeln('not equal', a - Abs(b));

    readln;
end.

produces not equal result, because of type conversions;

compiler watch reveals the same value for abs(vtemp1) and vtemp2

Try to add x : real, then call x := abs(b);, add x to watches list, select it and press Edit watch, then select Floating point. X becomes 0.899...967.

not only 0.09 value produces such result. you can try this code to check:

    for i := 0 to 10000 do begin
        a := a + 0.001;
        b := a;
        if a <> abs(b) then writeln('not equal', a);
    end;

so, if you need absolute value of Currency variable - just do it. don't use floating-point abs():

    function Abs(x : Currency):Currency; inline;
    begin
        if x > 0 then result := x
        else result := -x;
    end;
like image 99
teran Avatar answered Sep 19 '22 15:09

teran


A little clarification. The 'issue' appears if float values are compared:

var
  A: Currency;

begin
  A:= 0.09;
  Assert(A = Abs(A));
end;

That is because Abs(A) returns a float value, and A = Abs(A) is implemented as a float compare.

I could not reproduce it if Currency values are compared:

var
  A, B: Currency;
begin
  A:= 0.09;
  B:= Abs(A);
  Assert(A = B);
end;

But the second sample is also a potential bug because B:= Abs(A) internally is a float division/multiplication by 10000 with rounding to Currency (int64), and depends on FPU rounding mode.


I have created a qc report #107893, it was opened.

like image 20
kludg Avatar answered Sep 21 '22 15:09

kludg