Blame sysdeps/ieee754/dbl-64/s_sin.c

Packit 6c4009
/*
Packit 6c4009
 * IBM Accurate Mathematical Library
Packit 6c4009
 * written by International Business Machines Corp.
Packit 6c4009
 * Copyright (C) 2001-2018 Free Software Foundation, Inc.
Packit 6c4009
 *
Packit 6c4009
 * This program is free software; you can redistribute it and/or modify
Packit 6c4009
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c4009
 * the Free Software Foundation; either version 2.1 of the License, or
Packit 6c4009
 * (at your option) any later version.
Packit 6c4009
 *
Packit 6c4009
 * This program is distributed in the hope that it will be useful,
Packit 6c4009
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
 * GNU Lesser General Public License for more details.
Packit 6c4009
 *
Packit 6c4009
 * You should have received a copy of the GNU  Lesser General Public License
Packit 6c4009
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit 6c4009
 */
Packit 6c4009
/****************************************************************************/
Packit 6c4009
/*                                                                          */
Packit 6c4009
/* MODULE_NAME:usncs.c                                                      */
Packit 6c4009
/*                                                                          */
Packit 6c4009
/* FUNCTIONS: usin                                                          */
Packit 6c4009
/*            ucos                                                          */
Packit 6c4009
/* FILES NEEDED: dla.h endian.h mpa.h mydefs.h  usncs.h                     */
Packit 6c4009
/*		 branred.c sincos.tbl					    */
Packit 6c4009
/*                                                                          */
Packit 6c4009
/* An ultimate sin and cos routine. Given an IEEE double machine number x   */
Packit 6c4009
/* it computes sin(x) or cos(x) with ~0.55 ULP.				    */
Packit 6c4009
/* Assumption: Machine arithmetic operations are performed in               */
Packit 6c4009
/* round to nearest mode of IEEE 754 standard.                              */
Packit 6c4009
/*                                                                          */
Packit 6c4009
/****************************************************************************/
Packit 6c4009
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <float.h>
Packit 6c4009
#include "endian.h"
Packit 6c4009
#include "mydefs.h"
Packit 6c4009
#include "usncs.h"
Packit 6c4009
#include "MathLib.h"
Packit 6c4009
#include <math.h>
Packit 6c4009
#include <math_private.h>
Packit 6c4009
#include <math-underflow.h>
Packit 6c4009
#include <libm-alias-double.h>
Packit 6c4009
#include <fenv.h>
Packit 6c4009
Packit 6c4009
/* Helper macros to compute sin of the input values.  */
Packit 6c4009
#define POLYNOMIAL2(xx) ((((s5 * (xx) + s4) * (xx) + s3) * (xx) + s2) * (xx))
Packit 6c4009
Packit 6c4009
#define POLYNOMIAL(xx) (POLYNOMIAL2 (xx) + s1)
Packit 6c4009
Packit 6c4009
/* The computed polynomial is a variation of the Taylor series expansion for
Packit 6c4009
   sin(a):
Packit 6c4009
Packit 6c4009
   a - a^3/3! + a^5/5! - a^7/7! + a^9/9! + (1 - a^2) * da / 2
Packit 6c4009
Packit 6c4009
   The constants s1, s2, s3, etc. are pre-computed values of 1/3!, 1/5! and so
Packit 6c4009
   on.  The result is returned to LHS.  */
Packit 6c4009
#define TAYLOR_SIN(xx, a, da) \
Packit 6c4009
({									      \
Packit 6c4009
  double t = ((POLYNOMIAL (xx)  * (a) - 0.5 * (da))  * (xx) + (da));	      \
Packit 6c4009
  double res = (a) + t;							      \
Packit 6c4009
  res;									      \
Packit 6c4009
})
Packit 6c4009
Packit 6c4009
#define SINCOS_TABLE_LOOKUP(u, sn, ssn, cs, ccs) \
Packit 6c4009
({									      \
Packit 6c4009
  int4 k = u.i[LOW_HALF] << 2;						      \
Packit 6c4009
  sn = __sincostab.x[k];						      \
Packit 6c4009
  ssn = __sincostab.x[k + 1];						      \
Packit 6c4009
  cs = __sincostab.x[k + 2];						      \
Packit 6c4009
  ccs = __sincostab.x[k + 3];						      \
Packit 6c4009
})
Packit 6c4009
Packit 6c4009
#ifndef SECTION
Packit 6c4009
# define SECTION
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
extern const union
Packit 6c4009
{
Packit 6c4009
  int4 i[880];
Packit 6c4009
  double x[440];
Packit 6c4009
} __sincostab attribute_hidden;
Packit 6c4009
Packit 6c4009
static const double
Packit 6c4009
  sn3 = -1.66666666666664880952546298448555E-01,
Packit 6c4009
  sn5 = 8.33333214285722277379541354343671E-03,
Packit 6c4009
  cs2 = 4.99999999999999999999950396842453E-01,
Packit 6c4009
  cs4 = -4.16666666666664434524222570944589E-02,
Packit 6c4009
  cs6 = 1.38888874007937613028114285595617E-03;
Packit 6c4009
Packit 6c4009
int __branred (double x, double *a, double *aa);
Packit 6c4009
Packit 6c4009
/* Given a number partitioned into X and DX, this function computes the cosine
Packit 6c4009
   of the number by combining the sin and cos of X (as computed by a variation
Packit 6c4009
   of the Taylor series) with the values looked up from the sin/cos table to
Packit 6c4009
   get the result.  */
Packit 6c4009
static inline double
Packit 6c4009
__always_inline
Packit 6c4009
do_cos (double x, double dx)
Packit 6c4009
{
Packit 6c4009
  mynumber u;
Packit 6c4009
Packit 6c4009
  if (x < 0)
Packit 6c4009
    dx = -dx;
Packit 6c4009
Packit 6c4009
  u.x = big + fabs (x);
Packit 6c4009
  x = fabs (x) - (u.x - big) + dx;
Packit 6c4009
Packit 6c4009
  double xx, s, sn, ssn, c, cs, ccs, cor;
Packit 6c4009
  xx = x * x;
Packit 6c4009
  s = x + x * xx * (sn3 + xx * sn5);
Packit 6c4009
  c = xx * (cs2 + xx * (cs4 + xx * cs6));
Packit 6c4009
  SINCOS_TABLE_LOOKUP (u, sn, ssn, cs, ccs);
Packit 6c4009
  cor = (ccs - s * ssn - cs * c) - sn * s;
Packit 6c4009
  return cs + cor;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Given a number partitioned into X and DX, this function computes the sine of
Packit 6c4009
   the number by combining the sin and cos of X (as computed by a variation of
Packit 6c4009
   the Taylor series) with the values looked up from the sin/cos table to get
Packit 6c4009
   the result.  */
Packit 6c4009
static inline double
Packit 6c4009
__always_inline
Packit 6c4009
do_sin (double x, double dx)
Packit 6c4009
{
Packit 6c4009
  double xold = x;
Packit 6c4009
  /* Max ULP is 0.501 if |x| < 0.126, otherwise ULP is 0.518.  */
Packit 6c4009
  if (fabs (x) < 0.126)
Packit 6c4009
    return TAYLOR_SIN (x * x, x, dx);
Packit 6c4009
Packit 6c4009
  mynumber u;
Packit 6c4009
Packit 6c4009
  if (x <= 0)
Packit 6c4009
    dx = -dx;
Packit 6c4009
  u.x = big + fabs (x);
Packit 6c4009
  x = fabs (x) - (u.x - big);
Packit 6c4009
Packit 6c4009
  double xx, s, sn, ssn, c, cs, ccs, cor;
Packit 6c4009
  xx = x * x;
Packit 6c4009
  s = x + (dx + x * xx * (sn3 + xx * sn5));
Packit 6c4009
  c = x * dx + xx * (cs2 + xx * (cs4 + xx * cs6));
Packit 6c4009
  SINCOS_TABLE_LOOKUP (u, sn, ssn, cs, ccs);
Packit 6c4009
  cor = (ssn + s * ccs - sn * c) + cs * s;
Packit 6c4009
  return __copysign (sn + cor, xold);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Reduce range of x to within PI/2 with abs (x) < 105414350.  The high part
Packit 6c4009
   is written to *a, the low part to *da.  Range reduction is accurate to 136
Packit 6c4009
   bits so that when x is large and *a very close to zero, all 53 bits of *a
Packit 6c4009
   are correct.  */
Packit 6c4009
static inline int4
Packit 6c4009
__always_inline
Packit 6c4009
reduce_sincos (double x, double *a, double *da)
Packit 6c4009
{
Packit 6c4009
  mynumber v;
Packit 6c4009
Packit 6c4009
  double t = (x * hpinv + toint);
Packit 6c4009
  double xn = t - toint;
Packit 6c4009
  v.x = t;
Packit 6c4009
  double y = (x - xn * mp1) - xn * mp2;
Packit 6c4009
  int4 n = v.i[LOW_HALF] & 3;
Packit 6c4009
Packit 6c4009
  double b, db, t1, t2;
Packit 6c4009
  t1 = xn * pp3;
Packit 6c4009
  t2 = y - t1;
Packit 6c4009
  db = (y - t2) - t1;
Packit 6c4009
Packit 6c4009
  t1 = xn * pp4;
Packit 6c4009
  b = t2 - t1;
Packit 6c4009
  db += (t2 - b) - t1;
Packit 6c4009
Packit 6c4009
  *a = b;
Packit 6c4009
  *da = db;
Packit 6c4009
  return n;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Compute sin or cos (A + DA) for the given quadrant N.  */
Packit 6c4009
static double
Packit 6c4009
__always_inline
Packit 6c4009
do_sincos (double a, double da, int4 n)
Packit 6c4009
{
Packit 6c4009
  double retval;
Packit 6c4009
Packit 6c4009
  if (n & 1)
Packit 6c4009
    /* Max ULP is 0.513.  */
Packit 6c4009
    retval = do_cos (a, da);
Packit 6c4009
  else
Packit 6c4009
    /* Max ULP is 0.501 if xx < 0.01588, otherwise ULP is 0.518.  */
Packit 6c4009
    retval = do_sin (a, da);
Packit 6c4009
Packit 6c4009
  return (n & 2) ? -retval : retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*******************************************************************/
Packit 6c4009
/* An ultimate sin routine. Given an IEEE double machine number x   */
Packit 6c4009
/* it computes the correctly rounded (to nearest) value of sin(x)  */
Packit 6c4009
/*******************************************************************/
Packit 6c4009
#ifndef IN_SINCOS
Packit 6c4009
double
Packit 6c4009
SECTION
Packit 6c4009
__sin (double x)
Packit 6c4009
{
Packit 6c4009
  double t, a, da;
Packit 6c4009
  mynumber u;
Packit 6c4009
  int4 k, m, n;
Packit 6c4009
  double retval = 0;
Packit 6c4009
Packit 6c4009
  SET_RESTORE_ROUND_53BIT (FE_TONEAREST);
Packit 6c4009
Packit 6c4009
  u.x = x;
Packit 6c4009
  m = u.i[HIGH_HALF];
Packit 6c4009
  k = 0x7fffffff & m;		/* no sign           */
Packit 6c4009
  if (k < 0x3e500000)		/* if x->0 =>sin(x)=x */
Packit 6c4009
    {
Packit 6c4009
      math_check_force_underflow (x);
Packit 6c4009
      retval = x;
Packit 6c4009
    }
Packit 6c4009
/*--------------------------- 2^-26<|x|< 0.855469---------------------- */
Packit 6c4009
  else if (k < 0x3feb6000)
Packit 6c4009
    {
Packit 6c4009
      /* Max ULP is 0.548.  */
Packit 6c4009
      retval = do_sin (x, 0);
Packit 6c4009
    }				/*   else  if (k < 0x3feb6000)    */
Packit 6c4009
Packit 6c4009
/*----------------------- 0.855469  <|x|<2.426265  ----------------------*/
Packit 6c4009
  else if (k < 0x400368fd)
Packit 6c4009
    {
Packit 6c4009
      t = hp0 - fabs (x);
Packit 6c4009
      /* Max ULP is 0.51.  */
Packit 6c4009
      retval = __copysign (do_cos (t, hp1), x);
Packit 6c4009
    }				/*   else  if (k < 0x400368fd)    */
Packit 6c4009
Packit 6c4009
/*-------------------------- 2.426265<|x|< 105414350 ----------------------*/
Packit 6c4009
  else if (k < 0x419921FB)
Packit 6c4009
    {
Packit 6c4009
      n = reduce_sincos (x, &a, &da);
Packit 6c4009
      retval = do_sincos (a, da, n);
Packit 6c4009
    }				/*   else  if (k <  0x419921FB )    */
Packit 6c4009
Packit 6c4009
/* --------------------105414350 <|x| <2^1024------------------------------*/
Packit 6c4009
  else if (k < 0x7ff00000)
Packit 6c4009
    {
Packit 6c4009
      n = __branred (x, &a, &da);
Packit 6c4009
      retval = do_sincos (a, da, n);
Packit 6c4009
    }
Packit 6c4009
/*--------------------- |x| > 2^1024 ----------------------------------*/
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (k == 0x7ff00000 && u.i[LOW_HALF] == 0)
Packit 6c4009
	__set_errno (EDOM);
Packit 6c4009
      retval = x / x;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*******************************************************************/
Packit 6c4009
/* An ultimate cos routine. Given an IEEE double machine number x   */
Packit 6c4009
/* it computes the correctly rounded (to nearest) value of cos(x)  */
Packit 6c4009
/*******************************************************************/
Packit 6c4009
Packit 6c4009
double
Packit 6c4009
SECTION
Packit 6c4009
__cos (double x)
Packit 6c4009
{
Packit 6c4009
  double y, a, da;
Packit 6c4009
  mynumber u;
Packit 6c4009
  int4 k, m, n;
Packit 6c4009
Packit 6c4009
  double retval = 0;
Packit 6c4009
Packit 6c4009
  SET_RESTORE_ROUND_53BIT (FE_TONEAREST);
Packit 6c4009
Packit 6c4009
  u.x = x;
Packit 6c4009
  m = u.i[HIGH_HALF];
Packit 6c4009
  k = 0x7fffffff & m;
Packit 6c4009
Packit 6c4009
  /* |x|<2^-27 => cos(x)=1 */
Packit 6c4009
  if (k < 0x3e400000)
Packit 6c4009
    retval = 1.0;
Packit 6c4009
Packit 6c4009
  else if (k < 0x3feb6000)
Packit 6c4009
    {				/* 2^-27 < |x| < 0.855469 */
Packit 6c4009
      /* Max ULP is 0.51.  */
Packit 6c4009
      retval = do_cos (x, 0);
Packit 6c4009
    }				/*   else  if (k < 0x3feb6000)    */
Packit 6c4009
Packit 6c4009
  else if (k < 0x400368fd)
Packit 6c4009
    { /* 0.855469  <|x|<2.426265  */ ;
Packit 6c4009
      y = hp0 - fabs (x);
Packit 6c4009
      a = y + hp1;
Packit 6c4009
      da = (y - a) + hp1;
Packit 6c4009
      /* Max ULP is 0.501 if xx < 0.01588 or 0.518 otherwise.
Packit 6c4009
	 Range reduction uses 106 bits here which is sufficient.  */
Packit 6c4009
      retval = do_sin (a, da);
Packit 6c4009
    }				/*   else  if (k < 0x400368fd)    */
Packit 6c4009
Packit 6c4009
  else if (k < 0x419921FB)
Packit 6c4009
    {				/* 2.426265<|x|< 105414350 */
Packit 6c4009
      n = reduce_sincos (x, &a, &da);
Packit 6c4009
      retval = do_sincos (a, da, n + 1);
Packit 6c4009
    }				/*   else  if (k <  0x419921FB )    */
Packit 6c4009
Packit 6c4009
  /* 105414350 <|x| <2^1024 */
Packit 6c4009
  else if (k < 0x7ff00000)
Packit 6c4009
    {
Packit 6c4009
      n = __branred (x, &a, &da);
Packit 6c4009
      retval = do_sincos (a, da, n + 1);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (k == 0x7ff00000 && u.i[LOW_HALF] == 0)
Packit 6c4009
	__set_errno (EDOM);
Packit 6c4009
      retval = x / x;		/* |x| > 2^1024 */
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#ifndef __cos
Packit 6c4009
libm_alias_double (__cos, cos)
Packit 6c4009
#endif
Packit 6c4009
#ifndef __sin
Packit 6c4009
libm_alias_double (__sin, sin)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#endif