采取以下记录:
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);
我希望Boolean
的{{1}}值为(jHat2D = TVector2D.New(0,0.707).Normalised)
。然而它出现为True
。
在调试器False
中显示为TVector2D.New(0,0.707).Normalised.y
。
情况不一定是1,否则1
的{{1}}值将是Boolean
。
有什么想法吗?
修改
(jHat2D = TVector2D.New(0,0.707).Normalised)
是True
,定义为:Accuracy
答案 0 :(得分:8)
假设准确性是 Double 类型的同义词,这是调试器对浮点值进行可视化的错误。由于浮点内部表示的固有问题, v1.Y 和 v2.Y 的非常轻微不同的值,尽管两者都接近1。
为 v1.y 和 v2.y 添加监视。确保将这些监视值配置为表示为“浮点”值,并将数字设置为18以获得最大细节。
在断点处你会看到:
v1.y = 1
v2.y = 0.999999999999999889
( whosrdaddy 在问题的评论中提供了上述简短版本,但我保留了我的调查的长期形式 - 请参阅结论后面的行 - 作为它可能在其他类似的情况下以及潜在的利益中证明是有用的)
虽然调试器可视化严格地说是不正确的(或者最好是误导性的),但它们永远不会非常接近正确。 :)
接下来的问题是,您是否需要严格准确度或准确度以达到一定的容差。如果是后者,那么您可以采用 SameValue(),并根据您所需的准确度定义 EPSILON 。
否则,您必须接受在调试代码时,您不能依赖调试器来代表调试中涉及的值,以达到代码本身所依赖的准确度。
选项:自定义调试可视化
或者,您可能希望调查 TVector2D 类型的creating a custom debug visualisation,以表示您的代码中使用的准确度的x / y值。
对于使用 FloatToStr()的此类可视化,请使用带有%f 格式说明符的格式(),并使用合适的小数位数。例如以下调用产生通过观察变量获得的结果,如上所述:
Format('%.18f', [v2.y]);
// Yields 0.999999999999999889
我修改了等于操作符,以便我可以检查两个值 v1.y 和 v2.y 的内部表示形式:< / p>
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 ^ 的内存转储和 ,我们看到这两个值在内部表示非常不同:
v1.y : $3f f0 00 00 00 00 00 00
v2.y : $3f ef ff ff ff ff ff ff
注意:由于英特尔的Little Endian性质,与上述实际值相比,观察值结果中的字节顺序相反。
然后我们可以通过将双打与这些内部表示传递到 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.y 和 v2.y 不同。调试器对 Double 值的表示不正确(或者最好误导)。
通过更改 B 的表达式以使用 SameValue(),我们可以确定所涉及的值之间的偏差:
uses
Math;
const
EPSILON = 0.1;
B := SameValue(V1.y, V2.y, EPSILON);
通过逐步减少 EPSILON 的价值,我们发现 v1.y 和 v2.y 的差异小于 0.000000000000001 自:
EPSILON = 0.000000000000001; // Yields B = TRUE
EPSILON = 0.0000000000000001; // Yields B = FALSE
答案 1 :(得分:6)
您的问题源于这样的事实:2个浮点值不是100%相等,并且Debug Inspector对浮点进行舍入,以查看您需要添加监视并将浮点指定为可视化器的实际值:
使用内存转储可视化工具也会显示两个值之间的差异: