3

我写了一些代码来测试 fsqrt 函数,结果对我来说并不完全有意义。这是代码(在delphi中):

uses
 mmsystem;

var
 rand:longint=123456789;

function rng:longint;
asm
 imul eax,[rand],$08088405
 inc eax
 mov [rand],eax
end;

function int_sqrt(adata:longint):longint;
asm
 fnstcw word([esp-2])

// mov word([esp-4]),$1f3f  // 80bit precision
 mov word([esp-4]),$1c3f  // 24bit precision
 fldcw word([esp-4])

 mov [esp-8],eax
 fild longint([esp-8])

 fsqrt

 fistp longint([esp-8])
 mov eax,[esp-8]

 fldcw word([esp-2])
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 start,i,r,s1,s2:longint;
 time0,time1:longint;
begin
 timebeginperiod(1);
 time0:=timegettime;

 start:=1000000000;
 for i:=(start+0) to (start+100000000) do begin
  //r:=i;
  r:=abs(rng);
//  r:=2134567890;
//  r:=$7fffffff;
  s1:=int_sqrt(r);
  s2:=trunc(sqrt(r));
  if s1<>s2 then
   showmessage('error: '+inttostr(r)+'/'+inttostr(s1)+'/'+inttostr(s2));
 end;

 time1:=timegettime;
 timeendperiod(1);
 showmessage('Milliseconds: '+inttostr(time1-time0));
end;

很简单,我正在寻找一个 int 的平方根。在 int_sqrt 中,一条精度线让 x87 使用 24 位精度作为 sqrt 精度,另一个使用 64 位精度。正如预期的那样,24 位版本的速度要快很多(10-20% 取决于输入)。

这就是问题所在。我还没有找到使用 24 位精度时返回错误结果的单个 32 位(实际上是 31 位,最后一位是未使用的符号)int!

到目前为止,我唯一的理论是只有最终结果取决于精度,而不是源或任何中间缓冲区。这是有道理的,因为 31 位整数的平方根的最大结果大小是 16 位。

这是怎么回事?

4

1 回答 1

2

英特尔® 64 和 IA-32 架构软件开发人员手册卷。2A 第 3-291 页(FILD):

将有符号整数源操作数转换为双扩展精度浮点格式,并将值压入 FPU 寄存器堆栈。源操作数可以是字、双字或四字整数。它加载时没有舍入错误。

考虑到数据始终作为 80 位双扩展精度浮点数存储在 FPU中。FILD 和 FIST不会根据精度“忘记”位。精度的作用是在结果足够精确时中止计算,并在之后取消适当的位。

英特尔® 64 和 IA-32 架构软件开发人员手册卷。1 第 8.1.5.2 章(精密控制领域):

使用这些设置会抵消双扩展精度浮点格式的 64 位有效数字长度的优势。当指定降低精度时,有效数值的舍入会将右侧未使用的位清除为零。

所以FSQRT在完整的 80 位寄存器上工作并以 24 位的精度中止。我怀疑它会以 25 的精度中止以获得重要的舍入值。然后结果的“冗余” 60 位将被取消。你得到了一个 24 位的结果,这对于一个 16 位整数来说已经足够了,因为你注意到是正确的。

于 2014-05-31T17:04:32.897 回答