2

在下面的代码中,最后两次调用Ceil给出了意外的结果。你能帮忙评论一下原因吗?

此外,如果误差(或偏差)是随机的,我能得到预期值吗?

Ceil(Calculated_Var_Value) = 7Calculated_Var_Value = 7.0000000000.

非常感谢!

    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;
4

2 回答 2

4

这只是浮点运算固有的不准确性。您的两个值不能完全用二进制浮点表示,1.8并且-0.1. 因此,这些数字由最接近的可表示值近似。这意味着您的方程式很可能不会精确计算为7.

现在考虑你的两个表达式:

Val1 := (1.8 - 2.5) / -0.1;
Val2 := (A - B) / C;

这两者的区别在于Val1编译时Val2求值,运行时求值。现在,取决于编译器如何(1.8 - 2.5) / -0.1评估常量表达式。据我所知,它没有记录如何评估。

但是,很明显,编译器使用与运行时使用的不同的评估方法来评估常量表达式。该程序说明:

{$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.

输出:

7
8
真的
错误的
真的

因此,这表明Val1Val2具有不同的值,并且Val2严格大于7

您遇到的基本问题是浮点值的可表示性。因为您使用的是Extended使用二进制表示的 ,所以您的十进制输入值不能完全表示。如果你想在这里精确算术,你将需要使用十进制表示。

在回答这个问题的变体时,我向您推荐有关该主题的基本阅读材料:每个计算机科学家应该了解的浮点运算知识

于 2013-02-04T00:02:50.710 回答
2

这是典型的舍入误差。当您将此行添加到输出时,您可以看到它:

ShowMessage(FloatToStr(Val-7));
于 2013-02-04T00:02:03.540 回答