delphi - Embarcadero RAD Studio XE2 调试器中显示的局部变量的准确度是多少?显然1不等于1

标签 delphi delphi-xe2

进行以下记录:

TVector2D = record
  public
    class operator Equal(const V1, V2: TVector2D): Boolean;
    class operator Multiply(const D: Accuracy; const V: TVector2D): TVector2D;
    class operator Divide(const V: TVector2D; const D: Accuracy): TVector2D;
    class function New(const x, y: Accuracy): TVector2D; static;
    function Magnitude: Accuracy;
    function Normalised: TVector2D;
  public
    x, y: Accuracy;
  end;

方法定义为:

class operator TVector2D.Equal(const V1, V2: TVector2D): Boolean;
  var
    A, B: Boolean;
  begin
    Result := (V1.x = V2.x) and (V1.y = V2.y);
  end;

  class operator TVector2D.Multiply(const D: Accuracy; const V: TVector2D): TVector2D;
  begin
    Result.x := D*V.x;
    Result.y := D*V.y;
  end;

  class operator TVector2D.Divide(const V: TVector2D; const D: Accuracy): TVector2D;
  begin
    Result := (1.0/D)*V;
  end;

  class function TVector2D.New(const x, y: Accuracy): TVector2D;
  begin
    Result.x := x;
    Result.y := y;
  end;

  function TVector2D.Magnitude;
  begin
    RESULT := Sqrt(x*x + y*y);
  end;

  function TVector2D.Normalised: TVector2D;
  begin
    Result := Self/Magnitude;
  end;

和一个常数:

  const
    jHat2D : TVector2D = (x: 0; y: 1);

我希望 (jHat2D = TVector2D.New(0,0.707).Normalized)Boolean 值为 True。但结果却是False

在调试器中 TVector2D.New(0,0.707).Normalized.y 显示为 1

http://i.imgur.com/9XhQqgD.png

不可能正好为 1,否则 (jHat2D = TVector2D.New(0,0.707).Normalized)Boolean 值将为

有什么想法吗?

编辑

Accuracy 是一个Type,定义为:Accuracy = Double

最佳答案

假设AccuracyDouble类型的同义词,这是调试器可视化浮点值的一个错误。由于 float 内部表示的固有问题,v1.Yv2.Y 的值略有不同,尽管两者都近似于1.

添加v1.yv2.y的监视。确保将这些监视值配置为表示为“浮点”值,并将位数设置为 18,以获取最大细节。

在断点处您将看到:

v1.y      = 1
v2.y      = 0.999999999999999889

(whosrdaddy 在问题评论中提供了上述简短版本,但我保留了调查的详细形式 - 请参阅结论之后的行下方 - 作为它可能在其他类似的情况下有用并且具有潜在的兴趣)

结论

虽然严格来说调试器可视化是不正确的(或者充其量是误导性的),但它们仍然几乎正确。 :)

接下来的问题是您是否需要严格准确度或在一定容差范围内的准确度。如果是后者,那么您可以使用 SameValue() 以及定义的适合您所需准确度的 EPSILON

否则,您必须接受这样的事实:在调试代码时,您不能依赖调试器将调试中涉及的值表示为代码本身所依赖的准确度。

选项:自定义调试可视化本身

或者,您可能希望调查 creating a custom debug visualisation使您的 TVector2D 类型能够按照代码中使用的精度来表示 x/y 值。

对于使用 FloatToStr() 的此类可视化,请使用 Format() 以及带有适当小数位数的 %f 格式说明符。例如下面的调用产生通过观察上述变量获得的结果:

Format('%.18f', [v2.y]);

// Yields  0.999999999999999889
<小时/>

原始调查的长版本

我修改了 Equal 运算符,以允许我检查两个值 v1.yv2.y 的内部表示:

type
  PAccuracy = Accuracy;

class operator TVector2D.Equal(const V1, V2: TVector2D): Boolean;
var
  A, B: Boolean;
  ay, by: PAccuracy;
begin
  ay := @V1.y;
  by := @V2.y;

  A := (V1.x = V2.x);
  B := (V1.y = V2.y);

  result := A and B;
end;

通过在调试器中设置监视来提供 ay^by^内存转储,我们看到这两个值在内部表示非常不同:

v1.y   : $3f f0 00 00 00 00 00 00
v2.y   : $3f ef ff ff ff ff ff ff

注意:由于英特尔的 Little Endian 特性,与上述实际值相比,监视值结果中的字节顺序是相反的。

然后我们可以通过将带有这些内部表示的 double 传递给FloatToStr()来测试假设:

var
  a: Double;
  b: Double;
  ai: Int64 absolute a;
  bi: Int64 absolute b;

begin
  ai := $3ff0000000000000;
  bi := $3fefffffffffffff;

  s := FloatToStr(a) + ' = ' + FloatToStr(b);

  // Yields 's' = '1 = 1';
end;

因此我们可以得出结论,B的评估是正确的。 v1.yv2.y 不同。调试器对 Double 值的表示不正确(或者充其量是误导性的)。

通过更改 B 的表达式以使用 SameValue(),我们可以确定所涉及值之间的偏差:

uses
  Math;

const
  EPSILON = 0.1;

B := SameValue(V1.y, V2.y, EPSILON);

通过逐渐减小 EPSILON 的值,我们发现 v1.yv2.y 的差异小于 0.000000000000001 自:

EPSILON = 0.000000000000001;   // Yields B = TRUE
EPSILON = 0.0000000000000001;  // Yields B = FALSE

关于delphi - Embarcadero RAD Studio XE2 调试器中显示的局部变量的准确度是多少?显然1不等于1,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29972796/

相关文章:

delphi - 在Delphi 2010迁移项目中,Rave BE的“代码报告”替代方案是什么?

delphi - Delphi 中的 Web 应用程序

asp.net - 如何获取当前的登录用户,包括Delphi 2009中的域?

delphi - 使用密码加密 .INI 文件字符串的简单代码

delphi - 将多边形缩小为 TPoint 数组?

Delphi如何使用typedef void *参数调用C++ dll函数?

database - 如何将OOP技术应用到Delphi数据库编程中?

delphi - Indy TCP 服务器停用时卡住

delphi - Delphi:Vcl样式和拖放

HTML 标签解析