1

我在使用双精度变量计算简单算术方程时遇到问题。

我有一个具有双精度属性 Value 的组件,我将此属性设置为 100。

然后我做一个简单的减法来检查这个值是否真的是 100:

var
check: double;
begin
  check:= 100 - MyComponent.Value
  showmessage(floattostr(check));
end;

问题是我没有得到零,我得到 -1.4210854715202E-14,这是一个问题,因为我的程序检查这个结果是否正好为零

知道如何解决吗?

4

3 回答 3

5

尽管您另有声明,但返回的值MyComponent.Value显然不完全等于100. 如果是,那么100 - MyComponent.Value将完全等于0。我们可以说因为100可以用二进制浮点数精确表示。

很容易看到这一点100.0 - 100.0 = 0.0

var
  x, y: Double;
....
x := 100.0;
y := 100.0;
Assert(x-y=0.0);

你会发现,在你的场景中,

MyComponent.Value = 100.0

评估为False

一般来说,尝试精确比较浮点值总是一件危险的事情。特别是如果这些值是算术运算的结果,那么浮点运算固有的不精确性将意味着精确比较通常不会给出您期望的结果。

我假设MyComponent.Value实际上执行算术而不是像您声称的那样返回100.0.

有时检查浮点值是否相等的最佳方法是检查近似相等。例如,一些小的数字abs(x-y)<tol在哪里。tol这样做的困难在于很难提出一个可靠的选择 tol

在不了解更多细节的情况下,很难说你应该如何实施这个测试。

于 2013-07-27T12:58:55.167 回答
4

使用浮点数时,永远不要执行精确比较;始终使用小Epsilon值,如The Floating-Point Guide - What Every Programmer Should Know中所述。

注意:我特意在绝对意义上陈述这一点。当然也有例外,但实际上这些通常是例外的。当然,您可能处于您的问题领域非常特殊的情况,并且需要进行精确比较。在我维护的绝大多数代码中,情况并非如此。

Math单元(它与 Delphi 一起使用了很长时间)包含以下Epsilon为您处理值的函数。

当您将Epsilon零值(即0.0)或根本没有值传递给下面的函数时,它们将使用这些常量估计一个合理的值。

笔记:

您要使用的适当值Epsilon取决于您使用的计算:有时不准确会累积到比这些常数大得多的值。

const
  FuzzFactor = 1000;
  SingleResolution   = 1E-7 * FuzzFactor;
  DoubleResolution   = 1E-15 * FuzzFactor;
{$IFDEF EXTENDEDIS10BYTES}
  ExtendedResolution = 1E-19 * FuzzFactor;
{$ELSE  EXTENDEDIS10BYTES}
  ExtendedResolution = DoubleResolution;
{$ENDIF EXTENDEDIS10BYTES}

功能:

function CompareValue(const A, B: Extended; Epsilon: Extended = 0): TValueRelationship; overload;
function CompareValue(const A, B: Double; Epsilon: Double = 0): TValueRelationship; overload;
function CompareValue(const A, B: Single; Epsilon: Single = 0): TValueRelationship; overload;

function SameValue(const A, B: Extended; Epsilon: Extended = 0): Boolean; overload;
function SameValue(const A, B: Double; Epsilon: Double = 0): Boolean; overload;
function SameValue(const A, B: Single; Epsilon: Single = 0): Boolean; overload;

function IsZero(const A: Extended; Epsilon: Extended = 0): Boolean; overload;
function IsZero(const A: Double; Epsilon: Double = 0): Boolean; overload;
function IsZero(const A: Single; Epsilon: Single = 0): Boolean; overload;
于 2013-07-28T08:23:55.610 回答
3

当其中一个数字是浮点数时,建议您不要通过简单的减法检查零。取而代之的是,使用 IsZero 函数以您想要的任何精度(例如,0.00001)作为 Epsilon 参数。

procedure CheckFor100(MyPrecision);
begin
  if IsZero(100 - MyComponent.Value, MyPrecision) then
    ShowMessage('MyComponent value was 100')
  else
    ShowMessage('MyComponent value was not 100');
end;

或者,您可能希望考虑 SameValue 函数。我认为 IsZero 和 SameValue 都在数学单元中。

于 2013-07-28T00:52:08.933 回答