Ersal Şahin
Ersal Şahin

Reputation: 45

delphixe2 "type conversion error!" fractional results?

I wrote a code but it produces incorrect results!

Var
  a,d:double;
  c:currency;
  b:integer;
begin
 b:=10;
 c:=20.1;
 a:=30.1;
 d:=0;

 d:=a-b-c;

 Memo1.Lines.Add('a => '+FloatToStr(a));
 Memo1.Lines.Add('b => '+FloatToStr(b));
 Memo1.Lines.Add('c => '+FloatToStr(c));
 Memo1.Lines.Add('d => '+FloatToStr(d))
end;

Result:

a => 30.1
b => 10
c => 20.1
d => 1.4210854715202E-15

Results should be ZERO! Is this compiler type convertion error?

Is there a way or another?!!

Upvotes: 1

Views: 70

Answers (2)

David Heffernan
David Heffernan

Reputation: 612794

Well, the first thing to say is that you are playing with fire here. You are mixing Integer, Double and Currency in the same statement. That unholy mix is, frankly, asking for trouble.

On top of that, in case you do not already know, you should be aware that not all of your values are representable. Here are the source values:

  • a is type Double, value 30.1. This value is not exactly representable in binary floating point. The closest Double value is 30.10000000000000142108547152020037174224853515625.
  • b is type Integer, value 10. No surprises here.
  • c is type Currency, value 20.1. This is represented as a 64 bit integer with value 201000. This is a fixed point decimal data type. The representation includes an implicit shift of 10000.

Next your expression, a - b - c. It is evaluated left to right.

The code under x86 looks like this, with my annotations:

// load b into the floating point unit, converting from 32 bit integer
fild dword ptr [$00423ef0]
// subtract b from a, as floating point
fsubr qword ptr [$00423ed8]
// multiply by 10000 to convert to currency
fmul dword ptr [$0041c5e0]
// load c into the floating point unit, converting from 64 bit integer
fild qword ptr [$00423ee8]
// subtract c from (b - a)
fsubp st(1)
// divide by 10000, that is convert the result to a floating point value
fdiv dword ptr [$0041c5e0]
// store this floating point value into d
fstp qword ptr [$00423ee0]

The value that we store back into d is not equal to zero. Essentially that is because (a - b)*10000 <> 20100. Now, you might be surprised to learn that (a - b)*10000 <> 20100 but that's a natural consequence of the fact that a is not an exact representation of 30.1 as we saw in the first bullet point above.

I think that the moral of the tale is not to mix up binary and decimal operands in the same expressions. Or at least if you need to do so, be clear and precise about how you do it. For instance.

So, if you perform your calculations entirely in Currency then you will get the answer you expect. That's because Currency is a decimal representation and can store all of these values, and all intermediate values exactly.

Upvotes: 5

Ersal Şahin
Ersal Şahin

Reputation: 45

Var
      a,d:double;
      c:double;
      b:double;
    begin
     b:=10;
     c:=20.1;
     a:=30.1;
     d:=0;

     d:=a-b-c;

     Memo1.Lines.Add('a => '+FloatToStr(a));
     Memo1.Lines.Add('b => '+FloatToStr(b));
     Memo1.Lines.Add('c => '+FloatToStr(c));
     Memo1.Lines.Add('d => '+FloatToStr(d))
    end;

Result:

a => 30.1
b => 10
c => 20.1
d => 0

The problem of improving the variable of the same type !!!

"Double" type when assigning the value of different types (currency) if we use the error continues.

Upvotes: 0

Related Questions