1

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?!!

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490

2 Answers2

5

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • "d" double type variable "30.1" actually receives the value "30.10000000000000142108547152020037174224853515625" getting the value is amazing! Logically I can understand in a way that does not need to type conversion? – Ersal Şahin Oct 23 '14 at 10:43
  • I'm not sure what you mean. Are you asking for more help? – David Heffernan Oct 23 '14 at 10:55
  • 1
    I think the OP may want to take a look at [What Every Computer Scientist Should Know About Floating Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). – Leonardo Herrera Oct 24 '14 at 00:41
  • @DavidHeffernan I understand "double" made ​​to assign a value leads to incorrect results. If we can be fractional values ​​you assign "double" We need to use. thnks. – Ersal Şahin Oct 24 '14 at 08:59
0
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.