Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 9 Mar 2017 07:23:07 -0800
From:      Steve Kargl <sgk@troutmask.apl.washington.edu>
To:        Bruce Evans <brde@optusnet.com.au>
Cc:        freebsd-numerics@freebsd.org
Subject:   Re: Bit twiddling question
Message-ID:  <20170309152307.GA32781@troutmask.apl.washington.edu>
In-Reply-To: <20170309075236.GA30199@troutmask.apl.washington.edu>
References:  <20170308202417.GA23103@troutmask.apl.washington.edu> <20170309173152.F1269@besplex.bde.org> <20170309075236.GA30199@troutmask.apl.washington.edu>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, Mar 08, 2017 at 11:52:36PM -0800, Steve Kargl wrote:
> To give a hint at what I have been working on, I have implementations
> for sinpif(x), sinpi(x), cospif(x), and cospi(x).  For sinpif(x) and
> cospif(x) I have kernels that give correctly rounded results for 
> FLT_MIN <= x < 0x1p23 (at least on x86_64).   I also have slightly
> faster kernels that give max ULPs of about 0.5008.  For sinpi(x) and
> cospi(x), my kernels currently give about 1.25 ULP for the worse case
> error if I accumulate the polynomials in double precision.  If I 
> accumulate the polynomials in long double precision, then I get 
> correctly rounded results.  To complete the set, I was hoping to 
> work out ld80 and ld128 versions.  `ld128 is going to be problematic
> due to the absense of int128_t.
> 
> I'll send you what I have in a few days.

It is sometime amazing what happens when I sleep.  For sinpi[fl](x)
and the method I came up with one needs to do argument reduction
where x = n + r.  Here n is an integer and 0 < r < 1.  If n is even
then one is in the positive half-cyle of sin(x) and if n is odd then
one has the negative half-cycle.  For sinpif(x) this looks like

        if (ix < 0x4b000000) {          /* 1 <= |x| < 0x1p23 */
/* 1 */         ix = (uint32_t)ax;
/* 2 */         ax = ax == ix ? zero : __compute_sinpif(ax - ix);
/* 3 */         if (ix & 1) ax = -ax;
/* 4 */         return ((hx & 0x80000000) ? -ax : ax);
        }

Line 1 determines n.  Line 2 computes either sinpi(n) exactly or
sinpi(r).  Line 3 uses n odd to set the sign for the half-cycle.
Line 4 is used to set the sign from sin(-x) = -sin(x).

For double and ld80 one can use int64_t instead of uint32_t.  Last
night I realized that I don't need to use int64_t, and more importantly
I don't need int128_t.  The argument reduction can use uint32_t
everywhere.  For double, I currently have

        if (ix < 0x43300000) {          /* 1 <= |x| < 0x1p52 */
                int64_t n;
                n = (int64_t)ax;
                ax = ax == n ? zero : __compute_sinpi(ax - n);
                if (n & 1) ax = -ax;
                return ((hx & 0x80000000) ? -ax : ax);
        }

which can be written (off the top-of-head and not tested)

        if (ix < 0x43300000) {          /* 1 <= |x| < 0x1p52 */
                double volatile vx;
                uint32_t n;
                vx = ax + 0x1p52;
                vx = vx - 0x1p52;
                if (vx == ax) return(0);
                if (vx > UINT32_MAX) {   /* ax = m + n + r with m + n */
                    vx -= UINT32_MAX;    /* m = UINT32_MAX.  n is in range */
                    ax -= UINT32_MAX;    
				}
                n = (uint32_t)vx;
                ax = __compute_sinpi(ax - n);
                if (n & 1) ax = -ax;
                return ((hx & 0x80000000) ? -ax : ax);
        }

Something similar can be applied to ld128, but reduction may take
two rounds (ie., a comparison with UINT64_MAX and then UINT32_MAX).

-- 
Steve
20161221 https://www.youtube.com/watch?v=IbCHE-hONow



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20170309152307.GA32781>