delphi - Delphi 和 Free Pascal 中的 SQRT 函数的准确度如何?

标签 delphi fpc sqrt

SQRT 在 Delphi XE 中作为 80 位浮点值的 FPU 函数实现;不确定它在 64 位编译器中是如何实现的。众所周知,浮点函数是近似的。

我可以假设下一个断言永远不会失败吗?

procedure Test1(Value: Cardinal);
var
  Root: Cardinal;

begin
  Root:= Trunc(Sqrt(Value));
  Assert(Root * Root <= Value);
  if Root < $FFFF then
    Assert((Root + 1) * (Root + 1) > Value);
end;

procedure Test2(Value: UInt64);
var
  Root: UInt64;

begin
  Root:= Trunc(Sqrt(Value));
  Assert(Root * Root <= Value);
  if Root < $FFFFFFFF then
    Assert((Root + 1) * (Root + 1) > Value);
end;

最佳答案

实践多于理论:

对所有数字执行测试,如下所示:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

{$IFNDEF DEBUG}
  {$DEFINE DEBUG}
{$ENDIF}

procedure Test1(Value: Cardinal);
var
  Root: Cardinal;

begin
  Root:= Trunc(Sqrt(Value));
  Assert(Root * Root <= Value);
  if Root < $FFFF then
    Assert((Root + 1) * (Root + 1) > Value);
end;

procedure Test2(Value: UInt64);
var
  Root: UInt64;

begin
  Root:= Trunc(Sqrt(Value));
  Assert(Root * Root <= Value);
  if Root < $FFFFFFFF then
    Assert((Root + 1) * (Root + 1) > Value);
end;

var
  VCar: Cardinal;
  VUInt: UInt64;
const
  Limit1: Cardinal = $FFFFFFFF;
  Limit2: UInt64 = $FFFFFFFFFFFFFFFF;
begin
  try
    for VCar := 0 to Limit1 do
    begin
      if (VCar mod 10000000) = 0 then
        Writeln('VCarTest ', VCar, ' ', (VCar / Limit1 * 100):0:2, '%');
      Test1(VCar);
    end;
    Writeln('VCarTest 0 .. $', IntToHex(Limit1, 8), ' passed');
{ commented because cannot be executed in a reasonable time
    VUInt := 0;
    while (VUInt <= Limit2) do
    begin
      if (VUInt mod 2500000) = 0 then
        Writeln('VUIntTest ', VUInt, ' ', (VUInt / Limit2 * 100):0:2, '%');
      Test2(VUInt);
      Inc(VUInt);
    end;
    Writeln('VUIntTest ', VUInt);
    Writeln('All passed');
}

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

由于测试 UInt64 的整个范围确实需要年龄,因此我对测试进行了一些更改以测试所有完全平方数,每个平方之前的数字和之后的数字,只是为了使其更快并有更好的主意。我个人在 32 位上运行了一段时间的测试,没有失败(占整个测试的 1%),而在 64 位上,它很快就显示失败。我仍在进一步研究这一点,但我发布了代码以防万一您感兴趣:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

{$IFNDEF DEBUG}
  {$message error 'change your build configuration to Debug!'}
{$ENDIF}

procedure Test2(Value: UInt64);
var
  Root: UInt64;
begin
//try/except block only for 64 bits, since in 32 bits it makes the process much slower
{$ifdef CPUX64}
  try
{$endif}
    Root:= Trunc(Sqrt(Value));
    Assert(Root * Root <= Value);
    if Root < $FFFFFFFF then
      Assert((Root + 1) * (Root + 1) > Value);
{$ifdef CPUX64}
  except
    Writeln('Fails for value: ', Value, ' root: ', Root
      , ' test: ', (Root + 1) * (Root + 1));
    raise;
  end;
{$endif}
end;

var
  RUInt, VUInt: UInt64;

const
  Limit2: UInt64 = $FFFFFFFFFFF00000;
begin
  try
    RUInt := 1;
    repeat
      Inc(RUInt);
      VUInt := RUInt * RUInt;
      if (RUInt mod 2500000) = 0 then
        Writeln('VUIntTest ', VUInt, ' ', (VUInt / Limit2 * 100):0:4, '%');
      Test2(VUInt - 1);
      Test2(VUInt);
      Test2(VUInt + 1);
    until (VUInt >= Limit2);
    Writeln('VUIntTest ', VUInt);
    Writeln('All passed');
  except
    on E:EAssertionFailed do
      Writeln('The assertion failed for value ', VUInt, ' root base ', RUInt);
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

关于delphi - Delphi 和 Free Pascal 中的 SQRT 函数的准确度如何?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15306698/

相关文章:

c++ - NtEnumerateKey() 的 KeyInformation 参数

ios - 更新到XE2 Update 2后,Delphi/FPC代码不再在XCode下编译

delphi - GUI 应用程序中的空主窗体从 Delphi 转换为 Lazarus

windows - Delphi 中的 Sqrt 函数

python - 在 C 中使用 sqrt() 时遇到问题

java - 无法对基本类型 double 调用 sqrt(double)

delphi - 在项目的主要单元中使用条件 - IDE 会破坏代码

sql-server - 根据ADO提供程序格式化日期

interpreter - Free Pascal 有命令行解释器吗?

Delphi & Active Directory 安全组