glibc/sysdeps/ieee754/dbl-64/slowpow.c
Siddhesh Poyarekar 10e1cf6b73 Add systemtap markers to math function slow paths
Add systemtap probes to various slow paths in libm so that application
developers may use systemtap to find out if their applications are
hitting these slow paths.  We have added probes for pow, exp, log,
tan, atan and atan2.
2013-10-11 22:37:53 +05:30

126 lines
4.2 KiB
C

/*
* IBM Accurate Mathematical Library
* written by International Business Machines Corp.
* Copyright (C) 2001-2013 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*************************************************************************/
/* MODULE_NAME:slowpow.c */
/* */
/* FUNCTION:slowpow */
/* */
/*FILES NEEDED:mpa.h */
/* mpa.c mpexp.c mplog.c halfulp.c */
/* */
/* Given two IEEE double machine numbers y,x , routine computes the */
/* correctly rounded (to nearest) value of x^y. Result calculated by */
/* multiplication (in halfulp.c) or if result isn't accurate enough */
/* then routine converts x and y into multi-precision doubles and */
/* calls to mpexp routine */
/*************************************************************************/
#include "mpa.h"
#include <math_private.h>
#include <stap-probe.h>
#ifndef SECTION
# define SECTION
#endif
void __mpexp (mp_no *x, mp_no *y, int p);
void __mplog (mp_no *x, mp_no *y, int p);
double ulog (double);
double __halfulp (double x, double y);
double
SECTION
__slowpow (double x, double y, double z)
{
double res, res1;
mp_no mpx, mpy, mpz, mpw, mpp, mpr, mpr1;
static const mp_no eps = {-3, {1.0, 4.0}};
int p;
/* __HALFULP returns -10 or X^Y. */
res = __halfulp (x, y);
/* Return if the result was computed by __HALFULP. */
if (res >= 0)
return res;
/* Compute pow as long double. This is currently only used by powerpc, where
one may get 106 bits of accuracy. */
#ifdef USE_LONG_DOUBLE_FOR_MP
long double ldw, ldz, ldpp;
static const long double ldeps = 0x4.0p-96;
ldz = __ieee754_logl ((long double) x);
ldw = (long double) y *ldz;
ldpp = __ieee754_expl (ldw);
res = (double) (ldpp + ldeps);
res1 = (double) (ldpp - ldeps);
/* Return the result if it is accurate enough. */
if (res == res1)
return res;
#endif
/* Or else, calculate using multiple precision. P = 10 implies accuracy of
240 bits accuracy, since MP_NO has a radix of 2^24. */
p = 10;
__dbl_mp (x, &mpx, p);
__dbl_mp (y, &mpy, p);
__dbl_mp (z, &mpz, p);
/* z = x ^ y
log (z) = y * log (x)
z = exp (y * log (x)) */
__mplog (&mpx, &mpz, p);
__mul (&mpy, &mpz, &mpw, p);
__mpexp (&mpw, &mpp, p);
/* Add and subtract EPS to ensure that the result remains unchanged, i.e. we
have last bit accuracy. */
__add (&mpp, &eps, &mpr, p);
__mp_dbl (&mpr, &res, p);
__sub (&mpp, &eps, &mpr1, p);
__mp_dbl (&mpr1, &res1, p);
if (res == res1)
{
/* Track how often we get to the slow pow code plus
its input/output values. */
LIBC_PROBE (slowpow_p10, 4, &x, &y, &z, &res);
return res;
}
/* If we don't, then we repeat using a higher precision. 768 bits of
precision ought to be enough for anybody. */
p = 32;
__dbl_mp (x, &mpx, p);
__dbl_mp (y, &mpy, p);
__dbl_mp (z, &mpz, p);
__mplog (&mpx, &mpz, p);
__mul (&mpy, &mpz, &mpw, p);
__mpexp (&mpw, &mpp, p);
__mp_dbl (&mpp, &res, p);
/* Track how often we get to the uber-slow pow code plus
its input/output values. */
LIBC_PROBE (slowpow_p32, 4, &x, &y, &z, &res);
return res;
}