New __sqr function as a faster special case of __mul

This commit is contained in:
Siddhesh Poyarekar 2013-02-14 10:31:09 +05:30
parent 70d9946a44
commit d6752ccd69
8 changed files with 309 additions and 2 deletions

View File

@ -1,3 +1,16 @@
2013-02-14 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/ieee754/dbl-64/mpa.c (__sqr): New function.
* sysdeps/ieee754/dbl-64/mpa.h (__sqr): Declare.
* sysdeps/ieee754/dbl-64/mpexp.c (__mpexp): use __sqr instead
of __mul for squares.
* sysdeps/powerpc/powerpc32/power4/fpu/mpa.c (__sqr): New
function
* sysdeps/powerpc/powerpc64/power4/fpu/mpa.c (__sqr):
Likewise.
* sysdeps/x86_64/fpu/multiarch/mpa-avx.c: Define __sqr.
* sysdeps/x86_64/fpu/multiarch/mpa-fma4.c: Likewise.
2013-02-13 Joseph Myers <joseph@codesourcery.com>
[BZ #13550]

View File

@ -702,6 +702,97 @@ __mul (const mp_no *x, const mp_no *y, mp_no *z, int p)
Z[0] = X[0] * Y[0];
}
/* Square *X and store result in *Y. X and Y may not overlap. For P in
[1, 2, 3], the exact result is truncated to P digits. In case P > 3 the
error is bounded by 1.001 ULP. This is a faster special case of
multiplication. */
void
SECTION
__sqr (const mp_no *x, mp_no *y, int p)
{
long i, j, k, ip;
double u, yk;
/* Is z=0? */
if (__glibc_unlikely (X[0] == ZERO))
{
Y[0] = ZERO;
return;
}
/* We need not iterate through all X's since it's pointless to
multiply zeroes. */
for (ip = p; ip > 0; ip--)
if (X[ip] != ZERO)
break;
k = (__glibc_unlikely (p < 3)) ? p + p : p + 3;
while (k > 2 * ip + 1)
Y[k--] = ZERO;
yk = ZERO;
while (k > p)
{
double yk2 = 0.0;
long lim = k / 2;
if (k % 2 == 0)
{
yk += X[lim] * X[lim];
lim--;
}
for (i = k - p, j = p; i <= lim; i++, j--)
yk2 += X[i] * X[j];
yk += 2.0 * yk2;
u = (yk + CUTTER) - CUTTER;
if (u > yk)
u -= RADIX;
Y[k--] = yk - u;
yk = u * RADIXI;
}
while (k > 1)
{
double yk2 = 0.0;
long lim = k / 2;
if (k % 2 == 0)
{
yk += X[lim] * X[lim];
lim--;
}
for (i = 1, j = k - 1; i <= lim; i++, j--)
yk2 += X[i] * X[j];
yk += 2.0 * yk2;
u = (yk + CUTTER) - CUTTER;
if (u > yk)
u -= RADIX;
Y[k--] = yk - u;
yk = u * RADIXI;
}
Y[k] = yk;
/* Squares are always positive. */
Y[0] = 1.0;
EY = 2 * EX;
/* Is there a carry beyond the most significant digit? */
if (__glibc_unlikely (Y[1] == ZERO))
{
for (i = 1; i <= p; i++)
Y[i] = Y[i + 1];
EY--;
}
}
/* Invert *X and store in *Y. Relative error bound:
- For P = 2: 1.001 * R ^ (1 - P)
- For P = 3: 1.063 * R ^ (1 - P)

View File

@ -115,6 +115,7 @@ void __dbl_mp (double, mp_no *, int);
void __add (const mp_no *, const mp_no *, mp_no *, int);
void __sub (const mp_no *, const mp_no *, mp_no *, int);
void __mul (const mp_no *, const mp_no *, mp_no *, int);
void __sqr (const mp_no *, mp_no *, int);
void __dvd (const mp_no *, const mp_no *, mp_no *, int);
extern void __mpatan (mp_no *, mp_no *, int);

View File

@ -145,14 +145,14 @@ __mpexp (mp_no *x, mp_no *y, int p)
/* Raise polynomial value to the power of 2**m. Put result in y. */
for (k = 0, j = 0; k < m;)
{
__mul (&mpt2, &mpt2, &mpt1, p);
__sqr (&mpt2, &mpt1, p);
k++;
if (k == m)
{
j = 1;
break;
}
__mul (&mpt1, &mpt1, &mpt2, p);
__sqr (&mpt1, &mpt2, p);
k++;
}
if (j)

View File

@ -687,6 +687,106 @@ __mul (const mp_no *x, const mp_no *y, mp_no *z, int p)
return;
}
/* Square *X and store result in *Y. X and Y may not overlap. For P in
[1, 2, 3], the exact result is truncated to P digits. In case P > 3 the
error is bounded by 1.001 ULP. This is a faster special case of
multiplication. */
void
__sqr (const mp_no *x, mp_no *y, int p)
{
long i, j, k, ip;
double u, yk;
/* Is z=0? */
if (__glibc_unlikely (X[0] == ZERO))
{
Y[0] = ZERO;
return;
}
/* We need not iterate through all X's since it's pointless to
multiply zeroes. */
for (ip = p; ip > 0; ip--)
if (X[ip] != ZERO)
break;
k = (__glibc_unlikely (p < 3)) ? p + p : p + 3;
while (k > 2 * ip + 1)
Y[k--] = ZERO;
yk = ZERO;
while (k > p)
{
double yk2 = 0.0;
long lim = k / 2;
if (k % 2 == 0)
{
yk += X[lim] * X[lim];
lim--;
}
/* In __mul, this loop (and the one within the next while loop) run
between a range to calculate the mantissa as follows:
Z[k] = X[k] * Y[n] + X[k+1] * Y[n-1] ... + X[n-1] * Y[k+1]
+ X[n] * Y[k]
For X == Y, we can get away with summing halfway and doubling the
result. For cases where the range size is even, the mid-point needs
to be added separately (above). */
for (i = k - p, j = p; i <= lim; i++, j--)
yk2 += X[i] * X[j];
yk += 2.0 * yk2;
u = (yk + CUTTER) - CUTTER;
if (u > yk)
u -= RADIX;
Y[k--] = yk - u;
yk = u * RADIXI;
}
while (k > 1)
{
double yk2 = 0.0;
long lim = k / 2;
if (k % 2 == 0)
{
yk += X[lim] * X[lim];
lim--;
}
/* Likewise for this loop. */
for (i = 1, j = k - 1; i <= lim; i++, j--)
yk2 += X[i] * X[j];
yk += 2.0 * yk2;
u = (yk + CUTTER) - CUTTER;
if (u > yk)
u -= RADIX;
Y[k--] = yk - u;
yk = u * RADIXI;
}
Y[k] = yk;
/* Squares are always positive. */
Y[0] = 1.0;
EY = 2 * EX;
/* Is there a carry beyond the most significant digit? */
if (__glibc_unlikely (Y[1] == ZERO))
{
for (i = 1; i <= p; i++)
Y[i] = Y[i + 1];
EY--;
}
}
/* Invert *X and store in *Y. Relative error bound:
- For P = 2: 1.001 * R ^ (1 - P)
- For P = 3: 1.063 * R ^ (1 - P)

View File

@ -687,6 +687,106 @@ __mul (const mp_no *x, const mp_no *y, mp_no *z, int p)
return;
}
/* Square *X and store result in *Y. X and Y may not overlap. For P in
[1, 2, 3], the exact result is truncated to P digits. In case P > 3 the
error is bounded by 1.001 ULP. This is a faster special case of
multiplication. */
void
__sqr (const mp_no *x, mp_no *y, int p)
{
long i, j, k, ip;
double u, yk;
/* Is z=0? */
if (__glibc_unlikely (X[0] == ZERO))
{
Y[0] = ZERO;
return;
}
/* We need not iterate through all X's since it's pointless to
multiply zeroes. */
for (ip = p; ip > 0; ip--)
if (X[ip] != ZERO)
break;
k = (__glibc_unlikely (p < 3)) ? p + p : p + 3;
while (k > 2 * ip + 1)
Y[k--] = ZERO;
yk = ZERO;
while (k > p)
{
double yk2 = 0.0;
long lim = k / 2;
if (k % 2 == 0)
{
yk += X[lim] * X[lim];
lim--;
}
/* In __mul, this loop (and the one within the next while loop) run
between a range to calculate the mantissa as follows:
Z[k] = X[k] * Y[n] + X[k+1] * Y[n-1] ... + X[n-1] * Y[k+1]
+ X[n] * Y[k]
For X == Y, we can get away with summing halfway and doubling the
result. For cases where the range size is even, the mid-point needs
to be added separately (above). */
for (i = k - p, j = p; i <= lim; i++, j--)
yk2 += X[i] * X[j];
yk += 2.0 * yk2;
u = (yk + CUTTER) - CUTTER;
if (u > yk)
u -= RADIX;
Y[k--] = yk - u;
yk = u * RADIXI;
}
while (k > 1)
{
double yk2 = 0.0;
long lim = k / 2;
if (k % 2 == 0)
{
yk += X[lim] * X[lim];
lim--;
}
/* Likewise for this loop. */
for (i = 1, j = k - 1; i <= lim; i++, j--)
yk2 += X[i] * X[j];
yk += 2.0 * yk2;
u = (yk + CUTTER) - CUTTER;
if (u > yk)
u -= RADIX;
Y[k--] = yk - u;
yk = u * RADIXI;
}
Y[k] = yk;
/* Squares are always positive. */
Y[0] = 1.0;
EY = 2 * EX;
/* Is there a carry beyond the most significant digit? */
if (__glibc_unlikely (Y[1] == ZERO))
{
for (i = 1; i <= p; i++)
Y[i] = Y[i + 1];
EY--;
}
}
/* Invert *X and store in *Y. Relative error bound:
- For P = 2: 1.001 * R ^ (1 - P)
- For P = 3: 1.063 * R ^ (1 - P)

View File

@ -1,5 +1,6 @@
#define __add __add_avx
#define __mul __mul_avx
#define __sqr __sqr_avx
#define __sub __sub_avx
#define __dbl_mp __dbl_mp_avx
#define __dvd __dvd_avx

View File

@ -1,5 +1,6 @@
#define __add __add_fma4
#define __mul __mul_fma4
#define __sqr __sqr_fma4
#define __sub __sub_fma4
#define __dbl_mp __dbl_mp_fma4
#define __dvd __dvd_fma4