Reputation: 45
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
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
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