德尔福错误的双精度计算

时间:2013-07-27 12:56:43

标签: delphi floating-point

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

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

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

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

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

任何想法如何解决?

3 个答案:

答案 0 :(得分: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的强大选择。

如果不了解更多细节,很难说你应该如何实施这个测试。

答案 1 :(得分: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;

答案 2 :(得分:3)

建议您在其中一个数字为浮点数时,不要通过简单减法检查零。相反,对Epsilon参数使用IsZero函数,无论您需要什么精度(例如,0.00001)。

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都在数学单元中。