FPU instructions that check precision

315 Views Asked by At

Using the fldcw instruction it's possible to change the precision of the FPU unit to 24 or more bits. However after doing some testing I'm starting to think that very few x87 operations are in fact using that setting.

I haven't tested all operations but on this test machine so far, it looks like only fdiv and fsqrt stop computing at the selected precision, and that all other operations (fadd fsub fmul...) always compute full extended precision.

If that was the case I would expect it to be because those 2 instructions (fdiv and fsqrt) are significantly slower then most other x87 FPU instructions so when lower precision is sufficient it's possible to speed them up, but really, I'm just wondering if this has always been the case or if it's a quirk of the very recent processor used in my test machine.

edit: here's delphi code to show it

program Project1;

uses
  windows,dialogs,sysutils;

{$R *.res}

const
 test_mul:single=1234567890.0987654321;

var
 i:longint;
 s:single absolute i;
 s1,s2,s3:single;

procedure test_24;
asm
 mov word([esp-2]),$103f  // 24bit precision, trunc
 fldcw word([esp-2])

 fld [s]
 fmul [test_mul]
 fstp [s1]
end;

procedure test_53;
asm
 mov word([esp-2]),$123f  // 53bit precision, trunc
 fldcw word([esp-2])

 fld [s]
 fmul [test_mul]
 fstp [s2]
end;

procedure test_64;
asm
 mov word([esp-2]),$133f  // 64bit precision, trunc
 fldcw word([esp-2])

 fld [s]
 fmul [test_mul]
 fstp [s3]
end;

begin
 i:=0;
 repeat
  test_24;
  test_53;
  test_64;

  if (s1<>s2) or (s2<>s3) then begin
   showmessage('Error at step:'+inttostr(i));
   break;
  end;

  inc(i);
 until i=0;
 showmessage('No difference found between precisions');
end.

edit2: false alarm, I was mistaken, I was storing as single instead of extended so couldn't catch the difference, here's a fixed test, thanks to hans passant for catching my mistake:

program Project1;

uses
  windows,dialogs,sysutils;

{$R *.res}

const
 test_mul:single=1234567890.0987654321;

var
 i:longint;
 errors:cardinal;
 s:single absolute i;
 s1,s2,s3:extended;

procedure test_24;
asm
 mov word([esp-2]),$103f  // 24bit precision, trunc
 fldcw word([esp-2])

 fld [s]
 fmul [test_mul]
 fstp [s1]
end;

procedure test_53;
asm
 mov word([esp-2]),$123f  // 53bit precision, trunc
 fldcw word([esp-2])

 fld [s]
 fmul [test_mul]
 fstp [s2]
end;

procedure test_64;
asm
 mov word([esp-2]),$133f  // 64bit precision, trunc
 fldcw word([esp-2])

 fld [s]
 fmul [test_mul]
 fstp [s3]
end;

begin
 errors:=0;
 i:=0;
 repeat
  test_24;
  test_53;
  test_64;

  if (s1<>s2) or (s2<>s3) then begin
   inc(errors);
  end;

  inc(i);
 until i=0;
 showmessage('Number of differences between precisions: '+inttostr(errors));
end.
0

There are 0 best solutions below