Blame sysdeps/ieee754/dbl-64/sincos32.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
/*  MODULE_NAME: sincos32.c                                     */
Packit 6c4009
/*                                                              */
Packit 6c4009
/*  FUNCTIONS: ss32                                             */
Packit 6c4009
/*             cc32                                             */
Packit 6c4009
/*             c32                                              */
Packit 6c4009
/*             sin32                                            */
Packit 6c4009
/*             cos32                                            */
Packit 6c4009
/*             mpsin                                            */
Packit 6c4009
/*             mpcos                                            */
Packit 6c4009
/*             mpranred                                         */
Packit 6c4009
/*             mpsin1                                           */
Packit 6c4009
/*             mpcos1                                           */
Packit 6c4009
/*                                                              */
Packit 6c4009
/* FILES NEEDED: endian.h mpa.h sincos32.h                      */
Packit 6c4009
/*               mpa.c                                          */
Packit 6c4009
/*                                                              */
Packit 6c4009
/* Multi Precision sin() and cos() function with p=32  for sin()*/
Packit 6c4009
/* cos() arcsin() and arccos() routines                         */
Packit 6c4009
/* In addition mpranred() routine  performs range  reduction of */
Packit 6c4009
/* a double number x into multi precision number   y,           */
Packit 6c4009
/* such that y=x-n*pi/2, abs(y)
Packit 6c4009
/****************************************************************/
Packit 6c4009
#include "endian.h"
Packit 6c4009
#include "mpa.h"
Packit 6c4009
#include "sincos32.h"
Packit 6c4009
#include <math.h>
Packit 6c4009
#include <math_private.h>
Packit 6c4009
#include <stap-probe.h>
Packit 6c4009
Packit 6c4009
#ifndef SECTION
Packit 6c4009
# define SECTION
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Compute Multi-Precision sin() function for given p.  Receive Multi Precision
Packit 6c4009
   number x and result stored at y.  */
Packit 6c4009
static void
Packit 6c4009
SECTION
Packit 6c4009
ss32 (mp_no *x, mp_no *y, int p)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
  double a;
Packit 6c4009
  mp_no mpt1, x2, gor, sum, mpk = {1, {1.0}};
Packit 6c4009
  for (i = 1; i <= p; i++)
Packit 6c4009
    mpk.d[i] = 0;
Packit 6c4009
Packit 6c4009
  __sqr (x, &x2, p);
Packit 6c4009
  __cpy (&oofac27, &gor, p);
Packit 6c4009
  __cpy (&gor, &sum, p);
Packit 6c4009
  for (a = 27.0; a > 1.0; a -= 2.0)
Packit 6c4009
    {
Packit 6c4009
      mpk.d[1] = a * (a - 1.0);
Packit 6c4009
      __mul (&gor, &mpk, &mpt1, p);
Packit 6c4009
      __cpy (&mpt1, &gor, p);
Packit 6c4009
      __mul (&x2, &sum, &mpt1, p);
Packit 6c4009
      __sub (&gor, &mpt1, &sum, p);
Packit 6c4009
    }
Packit 6c4009
  __mul (x, &sum, y, p);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Compute Multi-Precision cos() function for given p. Receive Multi Precision
Packit 6c4009
   number x and result stored at y.  */
Packit 6c4009
static void
Packit 6c4009
SECTION
Packit 6c4009
cc32 (mp_no *x, mp_no *y, int p)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
  double a;
Packit 6c4009
  mp_no mpt1, x2, gor, sum, mpk = {1, {1.0}};
Packit 6c4009
  for (i = 1; i <= p; i++)
Packit 6c4009
    mpk.d[i] = 0;
Packit 6c4009
Packit 6c4009
  __sqr (x, &x2, p);
Packit 6c4009
  mpk.d[1] = 27.0;
Packit 6c4009
  __mul (&oofac27, &mpk, &gor, p);
Packit 6c4009
  __cpy (&gor, &sum, p);
Packit 6c4009
  for (a = 26.0; a > 2.0; a -= 2.0)
Packit 6c4009
    {
Packit 6c4009
      mpk.d[1] = a * (a - 1.0);
Packit 6c4009
      __mul (&gor, &mpk, &mpt1, p);
Packit 6c4009
      __cpy (&mpt1, &gor, p);
Packit 6c4009
      __mul (&x2, &sum, &mpt1, p);
Packit 6c4009
      __sub (&gor, &mpt1, &sum, p);
Packit 6c4009
    }
Packit 6c4009
  __mul (&x2, &sum, y, p);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Compute both sin(x), cos(x) as Multi precision numbers.  */
Packit 6c4009
void
Packit 6c4009
SECTION
Packit 6c4009
__c32 (mp_no *x, mp_no *y, mp_no *z, int p)
Packit 6c4009
{
Packit 6c4009
  mp_no u, t, t1, t2, c, s;
Packit 6c4009
  int i;
Packit 6c4009
  __cpy (x, &u, p);
Packit 6c4009
  u.e = u.e - 1;
Packit 6c4009
  cc32 (&u, &c, p);
Packit 6c4009
  ss32 (&u, &s, p);
Packit 6c4009
  for (i = 0; i < 24; i++)
Packit 6c4009
    {
Packit 6c4009
      __mul (&c, &s, &t, p);
Packit 6c4009
      __sub (&s, &t, &t1, p);
Packit 6c4009
      __add (&t1, &t1, &s, p);
Packit 6c4009
      __sub (&__mptwo, &c, &t1, p);
Packit 6c4009
      __mul (&t1, &c, &t2, p);
Packit 6c4009
      __add (&t2, &t2, &c, p);
Packit 6c4009
    }
Packit 6c4009
  __sub (&__mpone, &c, y, p);
Packit 6c4009
  __cpy (&s, z, p);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Receive double x and two double results of sin(x) and return result which is
Packit 6c4009
   more accurate, computing sin(x) with multi precision routine c32.  */
Packit 6c4009
double
Packit 6c4009
SECTION
Packit 6c4009
__sin32 (double x, double res, double res1)
Packit 6c4009
{
Packit 6c4009
  int p;
Packit 6c4009
  mp_no a, b, c;
Packit 6c4009
  p = 32;
Packit 6c4009
  __dbl_mp (res, &a, p);
Packit 6c4009
  __dbl_mp (0.5 * (res1 - res), &b, p);
Packit 6c4009
  __add (&a, &b, &c, p);
Packit 6c4009
  if (x > 0.8)
Packit 6c4009
    {
Packit 6c4009
      __sub (&hp, &c, &a, p);
Packit 6c4009
      __c32 (&a, &b, &c, p);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    __c32 (&c, &a, &b, p);	/* b=sin(0.5*(res+res1))  */
Packit 6c4009
  __dbl_mp (x, &c, p);		/* c = x  */
Packit 6c4009
  __sub (&b, &c, &a, p);
Packit 6c4009
  /* if a > 0 return min (res, res1), otherwise return max (res, res1).  */
Packit 6c4009
  if ((a.d[0] > 0 && res >= res1) || (a.d[0] <= 0 && res <= res1))
Packit 6c4009
    res = res1;
Packit 6c4009
  LIBC_PROBE (slowasin, 2, &res, &x);
Packit 6c4009
  return res;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Receive double x and two double results of cos(x) and return result which is
Packit 6c4009
   more accurate, computing cos(x) with multi precision routine c32.  */
Packit 6c4009
double
Packit 6c4009
SECTION
Packit 6c4009
__cos32 (double x, double res, double res1)
Packit 6c4009
{
Packit 6c4009
  int p;
Packit 6c4009
  mp_no a, b, c;
Packit 6c4009
  p = 32;
Packit 6c4009
  __dbl_mp (res, &a, p);
Packit 6c4009
  __dbl_mp (0.5 * (res1 - res), &b, p);
Packit 6c4009
  __add (&a, &b, &c, p);
Packit 6c4009
  if (x > 2.4)
Packit 6c4009
    {
Packit 6c4009
      __sub (&pi, &c, &a, p);
Packit 6c4009
      __c32 (&a, &b, &c, p);
Packit 6c4009
      b.d[0] = -b.d[0];
Packit 6c4009
    }
Packit 6c4009
  else if (x > 0.8)
Packit 6c4009
    {
Packit 6c4009
      __sub (&hp, &c, &a, p);
Packit 6c4009
      __c32 (&a, &c, &b, p);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    __c32 (&c, &b, &a, p);	/* b=cos(0.5*(res+res1))  */
Packit 6c4009
  __dbl_mp (x, &c, p);		/* c = x                  */
Packit 6c4009
  __sub (&b, &c, &a, p);
Packit 6c4009
  /* if a > 0 return max (res, res1), otherwise return min (res, res1).  */
Packit 6c4009
  if ((a.d[0] > 0 && res <= res1) || (a.d[0] <= 0 && res >= res1))
Packit 6c4009
    res = res1;
Packit 6c4009
  LIBC_PROBE (slowacos, 2, &res, &x);
Packit 6c4009
  return res;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Compute sin() of double-length number (X + DX) as Multi Precision number and
Packit 6c4009
   return result as double.  If REDUCE_RANGE is true, X is assumed to be the
Packit 6c4009
   original input and DX is ignored.  */
Packit 6c4009
double
Packit 6c4009
SECTION
Packit 6c4009
__mpsin (double x, double dx, bool reduce_range)
Packit 6c4009
{
Packit 6c4009
  double y;
Packit 6c4009
  mp_no a, b, c, s;
Packit 6c4009
  int n;
Packit 6c4009
  int p = 32;
Packit 6c4009
Packit 6c4009
  if (reduce_range)
Packit 6c4009
    {
Packit 6c4009
      n = __mpranred (x, &a, p);	/* n is 0, 1, 2 or 3.  */
Packit 6c4009
      __c32 (&a, &c, &s, p);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      n = -1;
Packit 6c4009
      __dbl_mp (x, &b, p);
Packit 6c4009
      __dbl_mp (dx, &c, p);
Packit 6c4009
      __add (&b, &c, &a, p);
Packit 6c4009
      if (x > 0.8)
Packit 6c4009
        {
Packit 6c4009
          __sub (&hp, &a, &b, p);
Packit 6c4009
          __c32 (&b, &s, &c, p);
Packit 6c4009
        }
Packit 6c4009
      else
Packit 6c4009
        __c32 (&a, &c, &s, p);	/* b = sin(x+dx)  */
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Convert result based on which quarter of unit circle y is in.  */
Packit 6c4009
  switch (n)
Packit 6c4009
    {
Packit 6c4009
    case 1:
Packit 6c4009
      __mp_dbl (&c, &y, p);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 3:
Packit 6c4009
      __mp_dbl (&c, &y, p);
Packit 6c4009
      y = -y;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 2:
Packit 6c4009
      __mp_dbl (&s, &y, p);
Packit 6c4009
      y = -y;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    /* Quadrant not set, so the result must be sin (X + DX), which is also in
Packit 6c4009
       S.  */
Packit 6c4009
    case 0:
Packit 6c4009
    default:
Packit 6c4009
      __mp_dbl (&s, &y, p);
Packit 6c4009
    }
Packit 6c4009
  LIBC_PROBE (slowsin, 3, &x, &dx, &y);
Packit 6c4009
  return y;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Compute cos() of double-length number (X + DX) as Multi Precision number and
Packit 6c4009
   return result as double.  If REDUCE_RANGE is true, X is assumed to be the
Packit 6c4009
   original input and DX is ignored.  */
Packit 6c4009
double
Packit 6c4009
SECTION
Packit 6c4009
__mpcos (double x, double dx, bool reduce_range)
Packit 6c4009
{
Packit 6c4009
  double y;
Packit 6c4009
  mp_no a, b, c, s;
Packit 6c4009
  int n;
Packit 6c4009
  int p = 32;
Packit 6c4009
Packit 6c4009
  if (reduce_range)
Packit 6c4009
    {
Packit 6c4009
      n = __mpranred (x, &a, p);	/* n is 0, 1, 2 or 3.  */
Packit 6c4009
      __c32 (&a, &c, &s, p);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      n = -1;
Packit 6c4009
      __dbl_mp (x, &b, p);
Packit 6c4009
      __dbl_mp (dx, &c, p);
Packit 6c4009
      __add (&b, &c, &a, p);
Packit 6c4009
      if (x > 0.8)
Packit 6c4009
        {
Packit 6c4009
          __sub (&hp, &a, &b, p);
Packit 6c4009
          __c32 (&b, &s, &c, p);
Packit 6c4009
        }
Packit 6c4009
      else
Packit 6c4009
        __c32 (&a, &c, &s, p);	/* a = cos(x+dx)     */
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Convert result based on which quarter of unit circle y is in.  */
Packit 6c4009
  switch (n)
Packit 6c4009
    {
Packit 6c4009
    case 1:
Packit 6c4009
      __mp_dbl (&s, &y, p);
Packit 6c4009
      y = -y;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 3:
Packit 6c4009
      __mp_dbl (&s, &y, p);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 2:
Packit 6c4009
      __mp_dbl (&c, &y, p);
Packit 6c4009
      y = -y;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    /* Quadrant not set, so the result must be cos (X + DX), which is also
Packit 6c4009
       stored in C.  */
Packit 6c4009
    case 0:
Packit 6c4009
    default:
Packit 6c4009
      __mp_dbl (&c, &y, p);
Packit 6c4009
    }
Packit 6c4009
  LIBC_PROBE (slowcos, 3, &x, &dx, &y);
Packit 6c4009
  return y;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Perform range reduction of a double number x into multi precision number y,
Packit 6c4009
   such that y = x - n * pi / 2, abs (y) < pi / 4, n = 0, +-1, +-2, ...
Packit 6c4009
   Return int which indicates in which quarter of circle x is.  */
Packit 6c4009
int
Packit 6c4009
SECTION
Packit 6c4009
__mpranred (double x, mp_no *y, int p)
Packit 6c4009
{
Packit 6c4009
  number v;
Packit 6c4009
  double t, xn;
Packit 6c4009
  int i, k, n;
Packit 6c4009
  mp_no a, b, c;
Packit 6c4009
Packit 6c4009
  if (fabs (x) < 2.8e14)
Packit 6c4009
    {
Packit 6c4009
      t = (x * hpinv.d + toint.d);
Packit 6c4009
      xn = t - toint.d;
Packit 6c4009
      v.d = t;
Packit 6c4009
      n = v.i[LOW_HALF] & 3;
Packit 6c4009
      __dbl_mp (xn, &a, p);
Packit 6c4009
      __mul (&a, &hp, &b, p);
Packit 6c4009
      __dbl_mp (x, &c, p);
Packit 6c4009
      __sub (&c, &b, y, p);
Packit 6c4009
      return n;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* If x is very big more precision required.  */
Packit 6c4009
      __dbl_mp (x, &a, p);
Packit 6c4009
      a.d[0] = 1.0;
Packit 6c4009
      k = a.e - 5;
Packit 6c4009
      if (k < 0)
Packit 6c4009
	k = 0;
Packit 6c4009
      b.e = -k;
Packit 6c4009
      b.d[0] = 1.0;
Packit 6c4009
      for (i = 0; i < p; i++)
Packit 6c4009
	b.d[i + 1] = toverp[i + k];
Packit 6c4009
      __mul (&a, &b, &c, p);
Packit 6c4009
      t = c.d[c.e];
Packit 6c4009
      for (i = 1; i <= p - c.e; i++)
Packit 6c4009
	c.d[i] = c.d[i + c.e];
Packit 6c4009
      for (i = p + 1 - c.e; i <= p; i++)
Packit 6c4009
	c.d[i] = 0;
Packit 6c4009
      c.e = 0;
Packit 6c4009
      if (c.d[1] >= HALFRAD)
Packit 6c4009
	{
Packit 6c4009
	  t += 1.0;
Packit 6c4009
	  __sub (&c, &__mpone, &b, p);
Packit 6c4009
	  __mul (&b, &hp, y, p);
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	__mul (&c, &hp, y, p);
Packit 6c4009
      n = (int) t;
Packit 6c4009
      if (x < 0)
Packit 6c4009
	{
Packit 6c4009
	  y->d[0] = -y->d[0];
Packit 6c4009
	  n = -n;
Packit 6c4009
	}
Packit 6c4009
      return (n & 3);
Packit 6c4009
    }
Packit 6c4009
}