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