https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93871

--- Comment #34 from Steve Kargl <sgk at troutmask dot apl.washington.edu> ---
On Fri, Feb 28, 2020 at 09:30:25AM +0000, thenlich at gcc dot gnu.org wrote:
>
> Going back one step, I wonder if it would be good enough
> to perform a correctly rounded conversion from degrees to
> radians, and just use sin() as is.
> 
>   ...
>      f = sgn * sin(deg2rad(arg))
>   end function fcn
> 
>   ! Compute correctly rounded value of x * pi / 180 for x = 0 ... 90
>   function deg2rad(x) result(y)
>     real(sp) y
>     real(sp), intent(in) :: x
>     real(sp), parameter :: a = atan(1._4) / 45
>     real(sp), parameter :: b = atan(1._16) / 45 - a
>     y = a * x + b * x
>   end function deg2rad
> 
> (The exact values of a and b still need some work)
> 

The only way I know of that will get the correctly rounded
value without punting to a higher precision is to decompose
the scaling and argumet to high and low precision, and then
do the multiplcation.

shi = high_bits_of(pi/180)  ! # of bits = half of precision
slo = pi/180 - shi
xhi = high_bits_of(arg)
xlo = arg - xhi

You then do

value = slo * xlo + slo * xhi + shi * xlo + shi * xhi

The last term is exact, and the summation is accumulated
from smallest terms to largest (ie., you get correct 
rounding). 

For float precision (and little endian), I use

static const float
deg2rad(float ax)
{
   /* Split pi/180 into 12 high bits and 24 low bits. */
   static const float pihi = 1.74560547e-02f, pilo =-2.76216747e-06f;

   float lo, hi;

   /* Need union to allow the clearly of low bits. */
   union {
      unsigned int u;
      float e;
   } v;

   v.e = ax;
   v.u = (v.u >> 12) << 12;  /* 12 high bits. */
   hi = v.e;                    
   lo = ax - hi;             /* low part.

   return (lo * pilo + lo * pihi + hi * pilo + hi * pihi);
}

Even this appears to have some irregularities as my exhaustive
test in the interval [1.e-8,1) with direct call to sinf() yields

count: 223622026
ulp > 1.0: 125
ulp > 1.5: 0
ulp > 1.6: 0
ulp > 1.7: 0
 max_ulp: 1.321531
x at max: 0.447627

Reply via email to