(root)/
mpfr-4.2.1/
src/
sin.c
       1  /* mpfr_sin -- sine of a floating-point number
       2  
       3  Copyright 2001-2023 Free Software Foundation, Inc.
       4  Contributed by the AriC and Caramba projects, INRIA.
       5  
       6  This file is part of the GNU MPFR Library.
       7  
       8  The GNU MPFR Library is free software; you can redistribute it and/or modify
       9  it under the terms of the GNU Lesser General Public License as published by
      10  the Free Software Foundation; either version 3 of the License, or (at your
      11  option) any later version.
      12  
      13  The GNU MPFR Library is distributed in the hope that it will be useful, but
      14  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
      15  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
      16  License for more details.
      17  
      18  You should have received a copy of the GNU Lesser General Public License
      19  along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
      20  https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
      21  51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
      22  
      23  #define MPFR_NEED_LONGLONG_H
      24  #include "mpfr-impl.h"
      25  
      26  static int
      27  mpfr_sin_fast (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode)
      28  {
      29    int inex;
      30  
      31    inex = mpfr_sincos_fast (y, NULL, x, rnd_mode);
      32    inex = inex & 3; /* 0: exact, 1: rounded up, 2: rounded down */
      33    return (inex == 2) ? -1 : inex;
      34  }
      35  
      36  int
      37  mpfr_sin (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode)
      38  {
      39    mpfr_t c, xr;
      40    mpfr_srcptr xx;
      41    mpfr_exp_t expx, err1, err;
      42    mpfr_prec_t precy, m;
      43    int inexact, sign, reduce;
      44    MPFR_ZIV_DECL (loop);
      45    MPFR_SAVE_EXPO_DECL (expo);
      46  
      47    MPFR_LOG_FUNC
      48      (("x[%Pd]=%.*Rg rnd=%d", mpfr_get_prec (x), mpfr_log_prec, x, rnd_mode),
      49       ("y[%Pd]=%.*Rg inexact=%d", mpfr_get_prec (y), mpfr_log_prec, y,
      50        inexact));
      51  
      52    if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x)))
      53      {
      54        if (MPFR_IS_NAN (x) || MPFR_IS_INF (x))
      55          {
      56            MPFR_SET_NAN (y);
      57            MPFR_RET_NAN;
      58          }
      59        else /* x is zero */
      60          {
      61            MPFR_ASSERTD (MPFR_IS_ZERO (x));
      62            MPFR_SET_ZERO (y);
      63            MPFR_SET_SAME_SIGN (y, x);
      64            MPFR_RET (0);
      65          }
      66      }
      67  
      68    expx = MPFR_GET_EXP (x);
      69    err1 = -2 * expx;
      70  
      71    /* sin(x) = x - x^3/6 + ... so the error is < 2^(3*EXP(x)-2) */
      72    MPFR_FAST_COMPUTE_IF_SMALL_INPUT (y, x, err1, 2, 0, rnd_mode, {});
      73  
      74    MPFR_SAVE_EXPO_MARK (expo);
      75  
      76    /* Compute initial precision */
      77    precy = MPFR_PREC (y);
      78  
      79    if (precy >= MPFR_SINCOS_THRESHOLD)
      80      {
      81        inexact = mpfr_sin_fast (y, x, rnd_mode);
      82        goto end;
      83      }
      84  
      85    /* for x large, since argument reduction is expensive, we want to avoid
      86       any failure in Ziv's strategy, thus we take into account expx too */
      87    m = precy + MPFR_INT_CEIL_LOG2 (MAX(precy,expx)) + 8;
      88  
      89    /* since we compute sin(x) as sqrt(1-cos(x)^2), and for x small we have
      90       cos(x)^2 ~ 1 - x^2, when subtracting cos(x)^2 from 1 we will lose
      91       about -2*expx bits if expx < 0 */
      92    if (expx < 0)
      93      {
      94        /* The following assertion includes a check for integer overflow.
      95           At this point, precy < MPFR_SINCOS_THRESHOLD, so that both m and
      96           err1 should be small enough. But the assertion makes the code
      97           safer (a smart compiler might be able to remove it). */
      98        MPFR_ASSERTN (err1 <= MPFR_PREC_MAX - m);
      99        m += err1;
     100      }
     101  
     102    if (expx >= 2)
     103      {
     104        mpfr_init2 (c, expx + m - 1);
     105        mpfr_init2 (xr, m);
     106      }
     107    else
     108      mpfr_init2 (c, m);
     109  
     110    MPFR_ZIV_INIT (loop, m);
     111    for (;;)
     112      {
     113        /* first perform argument reduction modulo 2*Pi (if needed),
     114           also helps to determine the sign of sin(x) */
     115        /* TODO: Perform range reduction in a way so that the sine can
     116           be computed directly from the cosine with sin(x)=cos(pi/2-x),
     117           without the need of sqrt(1 - x^2). */
     118        if (expx >= 2) /* If Pi < x < 4, we need to reduce too, to determine
     119                          the sign of sin(x). For 2 <= |x| < Pi, we could avoid
     120                          the reduction. */
     121          {
     122            reduce = 1;
     123            /* As expx + m - 1 will silently be converted into mpfr_prec_t
     124               in the mpfr_set_prec call, the assert below may be useful to
     125               avoid undefined behavior. */
     126            MPFR_ASSERTN (expx + m - 1 <= MPFR_PREC_MAX);
     127            mpfr_set_prec (c, expx + m - 1);
     128            mpfr_set_prec (xr, m);
     129            mpfr_const_pi (c, MPFR_RNDN);
     130            mpfr_mul_2ui (c, c, 1, MPFR_RNDN);
     131            mpfr_remainder (xr, x, c, MPFR_RNDN);
     132            /* The analysis is similar to that of cos.c:
     133               |xr - x - 2kPi| <= 2^(2-m). Thus we can decide the sign
     134               of sin(x) if xr is at distance at least 2^(2-m) of both
     135               0 and +/-Pi. */
     136            mpfr_div_2ui (c, c, 1, MPFR_RNDN);
     137            /* Since c approximates Pi with an error <= 2^(2-expx-m) <= 2^(-m),
     138               it suffices to check that c - |xr| >= 2^(2-m). */
     139            if (MPFR_IS_POS (xr))
     140              mpfr_sub (c, c, xr, MPFR_RNDZ);
     141            else
     142              mpfr_add (c, c, xr, MPFR_RNDZ);
     143            if (MPFR_IS_ZERO(xr)
     144                || MPFR_GET_EXP(xr) < (mpfr_exp_t) 3 - (mpfr_exp_t) m
     145                || MPFR_IS_ZERO(c)
     146                || MPFR_GET_EXP(c) < (mpfr_exp_t) 3 - (mpfr_exp_t) m)
     147              goto ziv_next;
     148  
     149            /* |xr - x - 2kPi| <= 2^(2-m), thus |sin(xr) - sin(x)| <= 2^(2-m) */
     150            xx = xr;
     151          }
     152        else /* the input argument is already reduced */
     153          {
     154            reduce = 0;
     155            xx = x;
     156          }
     157  
     158        sign = MPFR_SIGN(xx);
     159        /* now that the argument is reduced, precision m is enough */
     160        mpfr_set_prec (c, m);
     161        mpfr_cos (c, xx, MPFR_RNDA);    /* c = cos(x) rounded away */
     162        mpfr_sqr (c, c, MPFR_RNDU);     /* away */
     163        mpfr_ui_sub (c, 1, c, MPFR_RNDZ);
     164        mpfr_sqrt (c, c, MPFR_RNDZ);
     165        if (MPFR_IS_NEG_SIGN(sign))
     166          MPFR_CHANGE_SIGN(c);
     167  
     168        /* Warning: c may be 0! */
     169        if (MPFR_UNLIKELY (MPFR_IS_ZERO (c)))
     170          {
     171            /* Huge cancellation: increase prec a lot! */
     172            m = MAX (m, MPFR_PREC (x));
     173            m = 2 * m;
     174          }
     175        else
     176          {
     177            /* the absolute error on c is at most 2^(3-m-EXP(c)),
     178               plus 2^(2-m) if there was an argument reduction.
     179               Since EXP(c) <= 1, 3-m-EXP(c) >= 2-m, thus the error
     180               is at most 2^(3-m-EXP(c)) in case of argument reduction. */
     181            err = 2 * MPFR_GET_EXP (c) + (mpfr_exp_t) m - 3 - (reduce != 0);
     182            if (MPFR_CAN_ROUND (c, err, precy, rnd_mode))
     183              break;
     184  
     185            /* check for huge cancellation (Near 0) */
     186            if (err < (mpfr_exp_t) MPFR_PREC (y))
     187              m += MPFR_PREC (y) - err;
     188            /* Check if near 1 */
     189            if (MPFR_GET_EXP (c) == 1)
     190              m += m;
     191          }
     192  
     193      ziv_next:
     194        /* Else generic increase */
     195        MPFR_ZIV_NEXT (loop, m);
     196      }
     197    MPFR_ZIV_FREE (loop);
     198  
     199    inexact = mpfr_set (y, c, rnd_mode);
     200    /* inexact cannot be 0, since this would mean that c was representable
     201       within the target precision, but in that case mpfr_can_round will fail */
     202  
     203    mpfr_clear (c);
     204    if (expx >= 2)
     205      mpfr_clear (xr);
     206  
     207   end:
     208    MPFR_SAVE_EXPO_FREE (expo);
     209    return mpfr_check_range (y, inexact, rnd_mode);
     210  }