[Back to MISC SWAG index]  [Back to Main SWAG index]  [Original]

{
I have been working with Cleve Moler (from MathWorks) and Tim Coe about a sw
workaround for the Pentium FDIV bug.  Here is a BP version of the algorithm:

The FDIV_FIX unit defines a single function:


function fdiv(x: extended; y: extended): extended;

which will use about 25% more cycles than a single fdiv call, but always return
exact results for all double precision numbers, including the special cases of
Nan, Inf and denormal.
It also handles all extended precision numbers, giving a maximum error of a
single bit in the last position (1ulp).  This error can only be introduced if
the FDIV operations fails.
During the unit startup code, I test the FDIV instruction, and if if fails,
initialize a 16-byte table of critical nibble values.

If FDIV passes, the table is left empty, and no fixups will be done on
divisions.
=============== FDIV_FIX.PAS ===========================
}

 {$N+,E-,G+}
 Unit FDIV_FIX;

 Interface

 function fdiv(x, y : Extended): Extended;

 const
   fdiv_risc_table : array [0..15] of byte = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
   fdiv_scale : single = 0.9375;
   one_shl_63_l : Longint = $5f000000;
 var
   one_shl_63 : single absolute one_shl_63_l;

 Implementation

 function fdiv(x, y : Extended): Extended;
 Assembler;
 asm
   fld [x]
 @restart:
   mov bx,word ptr [y+6]
   fld [y]
   add bx,bx
    jnc @denormal
 {$IFOPT G+}
   shr bx,4
 {$ELSE}
   mov cl,4
   shr bx,cl
 {$ENDIF}
   cmp bl,255
    jne @ok
 {$IFOPT G+}
   shr bx,8
 {$ELSE}
   mov bl,bh
   and bx,255
 {$ENDIF}
   cmp byte ptr fdiv_risc_table[bx],bh
    jz @ok
   fld [fdiv_scale]
   fmul st(2),st
   fmulp st(1),st
    jmp @ok
 @denormal:
   or bx,word ptr [y]
   or bx,word ptr [y+2]
   or bx,word ptr [y+4]
    jz @zero
   fld [one_shl_63]
   fmul st(2),st
   fmulp st(1),st
   fstp [y]
    jmp @restart
 @zero:
 @ok:
   fdivp st(1),st
 end;

 const
   a_bug : single = 4195835.0;
   b_bug : single = 3145727.0;

 Procedure fdiv_init;
 var
   r : double;
   i : Integer;
 begin
   r := a_bug / b_bug;
   if a_bug - r * b_bug > 1.0 then begin
     i := 1;
     repeat
       fdiv_risc_table[i] := i;
       Inc(i,3);
     until i >= 16;
   end;
 end;

 begin
   fdiv_init;
 end.

[Back to MISC SWAG index]  [Back to Main SWAG index]  [Original]