mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-27 03:41:23 +08:00
10e1cf6b73
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.
126 lines
4.2 KiB
C
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;
|
|
}
|