correctly rounded sqrt() asm for x86 (i387)
authorRich Felker <dalias@aerifal.cx>
Thu, 15 Mar 2012 05:29:03 +0000 (01:29 -0400)
committerRich Felker <dalias@aerifal.cx>
Thu, 15 Mar 2012 05:29:03 +0000 (01:29 -0400)
the fsqrt opcode is correctly rounded, but only in the fpu's selected
precision mode, which is 80-bit extended precision. to get a correctly
rounded double precision output, we check for the only corner cases
where two-step rounding could give different results than one-step
(extended-precision mantissa ending in 0x400) and adjust the mantissa
slightly in the opposite direction of the rounding which the fpu
already did (reported in the c1 flag of the fpu status word).

this should have near-zero cost in the non-corner cases and at worst
very low cost.

note that in order for sqrt() to get used when compiling with gcc, the
broken, non-conformant builtin sqrt must be disabled.

src/math/i386/sqrt.s

index c6e553031de4f751337295a5f0d9e8b5b21065d5..8289d0940f6783d20eb17d9b6350504f46376cfe 100644 (file)
@@ -2,4 +2,20 @@
 .type sqrt,@function
 sqrt:  fldl 4(%esp)
        fsqrt
+       fstsw %ax
+       sub $12,%esp
+       fld %st(0)
+       fstpt (%esp)
+       mov (%esp),%ecx
+       and $0x7ff,%ecx
+       cmp $0x400,%ecx
+       jnz 1f
+       and $0x200,%eax
+       sub $0x100,%eax
+       sub %eax,(%esp)
+       fstp %st(0)
+       fldt (%esp)
+1:     add $12,%esp
+       fstpl 4(%esp)
+       fldl 4(%esp)
        ret