Reputation: 3852
In the following code, the last two calls to Ceil
give unexpected result. Could you help to comment on the reason?
Furthermore, if the error (or deviation) is random, could I get the expected value?
Ceil(Calculated_Var_Value) = 7
when Calculated_Var_Value = 7.0000000000
.
Many thanks!
procedure TForm2.FormCreate(Sender: TObject);
var
A, B, C: Extended;
Val: Extended;
begin
ShowMessage(FloatToStr((1.8 - 2.5) / -0.1));
ShowMessage(FloatToStrF((1.8 - 2.5) / -0.1, ffFixed, 20, 20));
ShowMessage(FloatToStr(Ceil((1.8 - 2.5) / -0.1)));
Val := (1.8 - 2.5) / -0.1;
ShowMessage(FloatToStr(Val));
ShowMessage(FloatToStrF(Val, ffFixed, 20, 20));
ShowMessage(FloatToStr(Ceil(Val)));
Val := (1.8 - 2.5) / -0.1;
ShowMessage(FloatToStr(Val * 100 / 100));
ShowMessage(FloatToStrF(Val * 100 / 100, ffFixed, 20, 20));
ShowMessage(FloatToStr(Ceil(Val * 100 / 100)));
A := 1.8; B := 2.5; C := -0.1;
Val := (A - B) / C;
ShowMessage(FloatToStr(Val));
ShowMessage(FloatToStrF(Val, ffFixed, 20, 20));
ShowMessage(FloatToStr(Ceil(Val)));
A := 1.8; B := 2.5; C := -0.1;
Val := (A - B) / C;
ShowMessage(FloatToStr(Val * 100 / 100));
ShowMessage(FloatToStrF(Val * 100 / 100, ffFixed, 20, 20));
ShowMessage(FloatToStr(Ceil(Val * 100 / 100)));
end;
Upvotes: 2
Views: 408
Reputation: 613202
This is just down to the inherent inaccuracy of floating point arithmetic. Two of your values are not exactly representable in binary floating point, 1.8
and -0.1
. So, those numbers are approximated by the closest representable values. And that means that it's quite plausible that your equation won't evaluate to exactly 7
.
Now consider your two expressions:
Val1 := (1.8 - 2.5) / -0.1;
Val2 := (A - B) / C;
The difference between these two is that Val1
is evaluated at compile time and Val2
is evaluated at runtime. Now, it's down to the compiler how the constant expression (1.8 - 2.5) / -0.1
is evaluated. To the best of my knowledge, it's not documented how that will be evaluated.
However, it is clear that the compiler uses a different evaluation method to evaluate the constant expression from that used at runtime. This program illustrates that:
{$APPTYPE CONSOLE}
uses
SysUtils, Math;
var
A, B, C: Extended;
Val1, Val2: Extended;
begin
Val1 := (1.8 - 2.5) / -0.1;
Writeln(Ceil(Val1));
A := 1.8; B := 2.5; C := -0.1;
Val2 := (A - B) / C;
Writeln(Ceil(Val2));
Writeln(BoolToStr(Val1=7.0, True));
Writeln(BoolToStr(Val2=7.0, True));
Writeln(BoolToStr(Val1<Val2, True));
Readln;
end.
Output:
7 8 True False True
So, this shows that Val1
and Val2
have different values, and that Val2
is strictly greater than 7
.
The fundamental problem you have is that of representably of floating point values. Because you are using Extended
, which uses a binary representation, your decimal input values are not exactly representable. If you want exact arithmetic here, you will need to use a decimal representation.
As always when answering variants of this question, I refer you to the essential reading on the subject: What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Upvotes: 4
Reputation: 47758
This is a typical rounding error. You can see it when you add this line to your output:
ShowMessage(FloatToStr(Val-7));
Upvotes: 2