示例#1
0
文件: atan.c 项目: sharugupta/OpenUH
double FN_PROTOTYPE(atan)(double x)
{

  /* Some constants and split constants. */

  static double piby2 = 1.5707963267948966e+00; /* 0x3ff921fb54442d18 */
  double chi, clo, v, s, q, z;

  /* Find properties of argument x. */

  unsigned long long ux, aux, xneg;
  GET_BITS_DP64(x, ux);
  aux = ux & ~SIGNBIT_DP64;
  xneg = (ux != aux);

  if (xneg) v = -x;
  else v = x;

  /* Argument reduction to range [-7/16,7/16] */

  if (aux < 0x3e50000000000000) /* v < 2.0^(-26) */
    {
      /* x is a good approximation to atan(x) and avoids working on
         intermediate denormal numbers */
      if (aux == 0x0000000000000000)
        return x;
      else
        return val_with_flags(x, AMD_F_INEXACT);
    }
  else if (aux > 0x4003800000000000) /* v > 39./16. */
    {

      if (aux > PINFBITPATT_DP64)
        {
          /* x is NaN */
#ifdef WINDOWS
          return handle_error("atan", ux|0x0008000000000000, _DOMAIN, 0,
                              EDOM, x, 0.0);
#else
          return x + x; /* Raise invalid if it's a signalling NaN */
#endif
        }
      else if (aux > 0x4370000000000000)
	{ /* abs(x) > 2^56 => arctan(1/x) is
	     insignificant compared to piby2 */
	  if (xneg)
            return val_with_flags(-piby2, AMD_F_INEXACT);
	  else
            return val_with_flags(piby2, AMD_F_INEXACT);
	}

      x = -1.0/v;
      /* (chi + clo) = arctan(infinity) */
      chi = 1.57079632679489655800e+00; /* 0x3ff921fb54442d18 */
      clo = 6.12323399573676480327e-17; /* 0x3c91a62633145c06 */
    }
  else if (aux > 0x3ff3000000000000) /* 39./16. > v > 19./16. */
    {
      x = (v-1.5)/(1.0+1.5*v);
      /* (chi + clo) = arctan(1.5) */
      chi = 9.82793723247329054082e-01; /* 0x3fef730bd281f69b */
      clo = 1.39033110312309953701e-17; /* 0x3c7007887af0cbbc */
    }
  else if (aux > 0x3fe6000000000000) /* 19./16. > v > 11./16. */
    {
      x = (v-1.0)/(1.0+v);
      /* (chi + clo) = arctan(1.) */
      chi = 7.85398163397448278999e-01; /* 0x3fe921fb54442d18 */
      clo = 3.06161699786838240164e-17; /* 0x3c81a62633145c06 */
    }
  else if (aux > 0x3fdc000000000000) /* 11./16. > v > 7./16. */
    {
      x = (2.0*v-1.0)/(2.0+v);
      /* (chi + clo) = arctan(0.5) */
      chi = 4.63647609000806093515e-01; /* 0x3fddac670561bb4f */
      clo = 2.26987774529616809294e-17; /* 0x3c7a2b7f222f65e0 */
    }
  else  /* v < 7./16. */
    {
      x = v;
      chi = 0.0;
      clo = 0.0;
    }

  /* Core approximation: Remez(4,4) on [-7/16,7/16] */

  s = x*x;
  q = x*s*
       (0.268297920532545909e0 +
	(0.447677206805497472e0 +
	 (0.220638780716667420e0 +
	  (0.304455919504853031e-1 +
	    0.142316903342317766e-3*s)*s)*s)*s)/
       (0.804893761597637733e0 +
	(0.182596787737507063e1 +
	 (0.141254259931958921e1 +
	  (0.424602594203847109e0 +
	    0.389525873944742195e-1*s)*s)*s)*s);

  z = chi - ((q - clo) - x);

  if (xneg) z = -z;
  return z;
}
示例#2
0
文件: atanh.c 项目: sharugupta/OpenUH
double FN_PROTOTYPE(atanh)(double x)
{

  unsigned long long ux, ax;
  double r, absx, t, poly;


  GET_BITS_DP64(x, ux);
  ax = ux & ~SIGNBIT_DP64;
  PUT_BITS_DP64(ax, absx);

  if ((ux & EXPBITS_DP64) == EXPBITS_DP64)
    {
      /* x is either NaN or infinity */
      if (ux & MANTBITS_DP64)
        {
          /* x is NaN */
#ifdef WINDOWS
          return handle_error(_FUNCNAME, ux|0x0008000000000000, _DOMAIN,
                              AMD_F_INVALID, EDOM, x, 0.0);
#else
          return x + x; /* Raise invalid if it is a signalling NaN */
#endif
        }
      else
        {
          /* x is infinity; return a NaN */
#ifdef WINDOWS
          return handle_error(_FUNCNAME, INDEFBITPATT_DP64, _DOMAIN,
                              AMD_F_INVALID, EDOM, x, 0.0);
#else
          return retval_errno_edom(x,nan_with_flags(AMD_F_INVALID));
#endif
        }
    }
  else if (ax >= 0x3ff0000000000000)
    {
      if (ax > 0x3ff0000000000000)
        {
          /* abs(x) > 1.0; return NaN */
#ifdef WINDOWS
          return handle_error(_FUNCNAME, INDEFBITPATT_DP64, _DOMAIN,
                              AMD_F_INVALID, EDOM, x, 0.0);
#else
          return retval_errno_edom(x,nan_with_flags(AMD_F_INVALID));
#endif
        }
      else if (ux == 0x3ff0000000000000)
        {
          /* x = +1.0; return infinity with the same sign as x
             and set the divbyzero status flag */
#ifdef WINDOWS
          return handle_error(_FUNCNAME, PINFBITPATT_DP64, _DOMAIN,
                              AMD_F_INVALID, EDOM, x, 0.0);
#else
          return retval_errno_edom(x,infinity_with_flags(AMD_F_DIVBYZERO));
#endif
        }
      else
        {
          /* x = -1.0; return infinity with the same sign as x */
#ifdef WINDOWS
          return handle_error(_FUNCNAME, NINFBITPATT_DP64, _DOMAIN,
                              AMD_F_INVALID, EDOM, x, 0.0);
#else
          return retval_errno_edom(x,-infinity_with_flags(AMD_F_DIVBYZERO));
#endif
        }
    }


  if (ax < 0x3e30000000000000)
    {
      if (ax == 0x0000000000000000)
        {
          /* x is +/-zero. Return the same zero. */
          return x;
        }
      else
        {
          /* Arguments smaller than 2^(-28) in magnitude are
             approximated by atanh(x) = x, raising inexact flag. */
          return val_with_flags(x, AMD_F_INEXACT);
        }
    }
  else
    {
      if (ax < 0x3fe0000000000000)
        {
          /* Arguments up to 0.5 in magnitude are
             approximated by a [5,5] minimax polynomial */
          t = x*x;
          poly =
            (0.47482573589747356373e0 +
             (-0.11028356797846341457e1 +
              (0.88468142536501647470e0 +
               (-0.28180210961780814148e0 +
                (0.28728638600548514553e-1 -
                 0.10468158892753136958e-3 * t) * t) * t) * t) * t) /
            (0.14244772076924206909e1 +
             (-0.41631933639693546274e1 +
              (0.45414700626084508355e1 +
               (-0.22608883748988489342e1 +
                (0.49561196555503101989e0 -
                 0.35861554370169537512e-1 * t) * t) * t) * t) * t);
          return x + x*t*poly;
        }
      else
        {
          /* abs(x) >= 0.5 */
          /* Note that
               atanh(x) = 0.5 * ln((1+x)/(1-x))
             (see Abramowitz and Stegun 4.6.22).
             For greater accuracy we use the variant formula
             atanh(x) = log(1 + 2x/(1-x)) = log1p(2x/(1-x)).
          */
          r = (2.0 * absx) / (1.0 - absx);
          r = 0.5 * FN_PROTOTYPE(log1p)(r);
          if (ux & SIGNBIT_DP64)
            /* Argument x is negative */
            return -r;
          else
            return r;
        }
    }
}
示例#3
0
double FN_PROTOTYPE(sinh)(double x)
{
  /*
    After dealing with special cases the computation is split into
    regions as follows:

    abs(x) >= max_sinh_arg:
    sinh(x) = sign(x)*Inf

    abs(x) >= small_threshold:
    sinh(x) = sign(x)*exp(abs(x))/2 computed using the
    splitexp and scaleDouble functions as for exp_amd().

    abs(x) < small_threshold:
    compute p = exp(y) - 1 and then z = 0.5*(p+(p/(p+1.0)))
    sinh(x) is then sign(x)*z.                             */

  static const double
    max_sinh_arg = 7.10475860073943977113e+02, /* 0x408633ce8fb9f87e */
    thirtytwo_by_log2 = 4.61662413084468283841e+01, /* 0x40471547652b82fe */
    log2_by_32_lead = 2.16608493356034159660e-02, /* 0x3f962e42fe000000 */
    log2_by_32_tail = 5.68948749532545630390e-11, /* 0x3dcf473de6af278e */
    small_threshold = 8*BASEDIGITS_DP64*0.30102999566398119521373889;
  /* (8*BASEDIGITS_DP64*log10of2) ' exp(-x) insignificant c.f. exp(x) */

  /* Lead and tail tabulated values of sinh(i) and cosh(i) 
     for i = 0,...,36. The lead part has 26 leading bits. */

  static const double sinh_lead[37] = {
    0.00000000000000000000e+00,  /* 0x0000000000000000 */
    1.17520117759704589844e+00,  /* 0x3ff2cd9fc0000000 */
    3.62686038017272949219e+00,  /* 0x400d03cf60000000 */
    1.00178747177124023438e+01,  /* 0x40240926e0000000 */
    2.72899169921875000000e+01,  /* 0x403b4a3800000000 */
    7.42032089233398437500e+01,  /* 0x40528d0160000000 */
    2.01713153839111328125e+02,  /* 0x406936d228000000 */
    5.48316116333007812500e+02,  /* 0x4081228768000000 */
    1.49047882080078125000e+03,  /* 0x409749ea50000000 */
    4.05154187011718750000e+03,  /* 0x40afa71570000000 */
    1.10132326660156250000e+04,  /* 0x40c5829dc8000000 */
    2.99370708007812500000e+04,  /* 0x40dd3c4488000000 */
    8.13773945312500000000e+04,  /* 0x40f3de1650000000 */
    2.21206695312500000000e+05,  /* 0x410b00b590000000 */
    6.01302140625000000000e+05,  /* 0x412259ac48000000 */
    1.63450865625000000000e+06,  /* 0x4138f0cca8000000 */
    4.44305525000000000000e+06,  /* 0x4150f2ebd0000000 */
    1.20774762500000000000e+07,  /* 0x4167093488000000 */
    3.28299845000000000000e+07,  /* 0x417f4f2208000000 */
    8.92411500000000000000e+07,  /* 0x419546d8f8000000 */
    2.42582596000000000000e+08,  /* 0x41aceb0888000000 */
    6.59407856000000000000e+08,  /* 0x41c3a6e1f8000000 */
    1.79245641600000000000e+09,  /* 0x41dab5adb8000000 */
    4.87240166400000000000e+09,  /* 0x41f226af30000000 */
    1.32445608960000000000e+10,  /* 0x4208ab7fb0000000 */
    3.60024494080000000000e+10,  /* 0x4220c3d390000000 */
    9.78648043520000000000e+10,  /* 0x4236c93268000000 */
    2.66024116224000000000e+11,  /* 0x424ef822f0000000 */
    7.23128516608000000000e+11,  /* 0x42650bba30000000 */
    1.96566712320000000000e+12,  /* 0x427c9aae40000000 */
    5.34323724288000000000e+12,  /* 0x4293704708000000 */
    1.45244246507520000000e+13,  /* 0x42aa6b7658000000 */
    3.94814795284480000000e+13,  /* 0x42c1f43fc8000000 */
    1.07321789251584000000e+14,  /* 0x42d866f348000000 */
    2.91730863685632000000e+14,  /* 0x42f0953e28000000 */
    7.93006722514944000000e+14,  /* 0x430689e220000000 */
    2.15561576592179200000e+15}; /* 0x431ea215a0000000 */

  static const double sinh_tail[37] = {
    0.00000000000000000000e+00,  /* 0x0000000000000000 */
    1.60467555584448807892e-08,  /* 0x3e513ae6096a0092 */
    2.76742892754807136947e-08,  /* 0x3e5db70cfb79a640 */
    2.09697499555224576530e-07,  /* 0x3e8c2526b66dc067 */
    2.04940252448908240062e-07,  /* 0x3e8b81b18647f380 */
    1.65444891522700935932e-06,  /* 0x3ebbc1cdd1e1eb08 */
    3.53116789999998198721e-06,  /* 0x3ecd9f201534fb09 */
    6.94023870987375490695e-06,  /* 0x3edd1c064a4e9954 */
    4.98876893611587449271e-06,  /* 0x3ed4eca65d06ea74 */
    3.19656024605152215752e-05,  /* 0x3f00c259bcc0ecc5 */
    2.08687768377236501204e-04,  /* 0x3f2b5a6647cf9016 */
    4.84668088325403796299e-05,  /* 0x3f09691adefb0870 */
    1.17517985422733832468e-03,  /* 0x3f53410fc29cde38 */
    6.90830086959560562415e-04,  /* 0x3f46a31a50b6fb3c */
    1.45697262451506548420e-03,  /* 0x3f57defc71805c40 */
    2.99859023684906737806e-02,  /* 0x3f9eb49fd80e0bab */
    1.02538800507941396667e-02,  /* 0x3f84fffc7bcd5920 */
    1.26787628407699110022e-01,  /* 0x3fc03a93b6c63435 */
    6.86652479544033744752e-02,  /* 0x3fb1940bb255fd1c */
    4.81593627621056619148e-01,  /* 0x3fded26e14260b50 */
    1.70489513795397629181e+00,  /* 0x3ffb47401fc9f2a2 */
    1.12416073482258713767e+01,  /* 0x40267bb3f55634f1 */
    7.06579578070110514432e+00,  /* 0x401c435ff8194ddc */
    5.91244512999659974639e+01,  /* 0x404d8fee052ba63a */
    1.68921736147050694399e+02,  /* 0x40651d7edccde3f6 */
    2.60692936262073658327e+02,  /* 0x40704b1644557d1a */
    3.62419382134885609048e+02,  /* 0x4076a6b5ca0a9dc4 */
    4.07689930834187271103e+03,  /* 0x40afd9cc72249aba */
    1.55377375868385224749e+04,  /* 0x40ce58de693edab5 */
    2.53720210371943067003e+04,  /* 0x40d8c70158ac6363 */
    4.78822310734952334315e+04,  /* 0x40e7614764f43e20 */
    1.81871712615542812273e+05,  /* 0x4106337db36fc718 */
    5.62892347580489004031e+05,  /* 0x41212d98b1f611e2 */
    6.41374032312148716301e+05,  /* 0x412392bc108b37cc */
    7.57809544070145115256e+06,  /* 0x415ce87bdc3473dc */
    3.64177136406482197344e+06,  /* 0x414bc8d5ae99ad14 */
    7.63580561355670914054e+06}; /* 0x415d20d76744835c */

  static const double cosh_lead[37] = {
    1.00000000000000000000e+00,  /* 0x3ff0000000000000 */
    1.54308062791824340820e+00,  /* 0x3ff8b07550000000 */
    3.76219564676284790039e+00,  /* 0x400e18fa08000000 */
    1.00676617622375488281e+01,  /* 0x402422a490000000 */
    2.73082327842712402344e+01,  /* 0x403b4ee858000000 */
    7.42099475860595703125e+01,  /* 0x40528d6fc8000000 */
    2.01715633392333984375e+02,  /* 0x406936e678000000 */
    5.48317031860351562500e+02,  /* 0x4081228948000000 */
    1.49047915649414062500e+03,  /* 0x409749eaa8000000 */
    4.05154199218750000000e+03,  /* 0x40afa71580000000 */
    1.10132329101562500000e+04,  /* 0x40c5829dd0000000 */
    2.99370708007812500000e+04,  /* 0x40dd3c4488000000 */
    8.13773945312500000000e+04,  /* 0x40f3de1650000000 */
    2.21206695312500000000e+05,  /* 0x410b00b590000000 */
    6.01302140625000000000e+05,  /* 0x412259ac48000000 */
    1.63450865625000000000e+06,  /* 0x4138f0cca8000000 */
    4.44305525000000000000e+06,  /* 0x4150f2ebd0000000 */
    1.20774762500000000000e+07,  /* 0x4167093488000000 */
    3.28299845000000000000e+07,  /* 0x417f4f2208000000 */
    8.92411500000000000000e+07,  /* 0x419546d8f8000000 */
    2.42582596000000000000e+08,  /* 0x41aceb0888000000 */
    6.59407856000000000000e+08,  /* 0x41c3a6e1f8000000 */
    1.79245641600000000000e+09,  /* 0x41dab5adb8000000 */
    4.87240166400000000000e+09,  /* 0x41f226af30000000 */
    1.32445608960000000000e+10,  /* 0x4208ab7fb0000000 */
    3.60024494080000000000e+10,  /* 0x4220c3d390000000 */
    9.78648043520000000000e+10,  /* 0x4236c93268000000 */
    2.66024116224000000000e+11,  /* 0x424ef822f0000000 */
    7.23128516608000000000e+11,  /* 0x42650bba30000000 */
    1.96566712320000000000e+12,  /* 0x427c9aae40000000 */
    5.34323724288000000000e+12,  /* 0x4293704708000000 */
    1.45244246507520000000e+13,  /* 0x42aa6b7658000000 */
    3.94814795284480000000e+13,  /* 0x42c1f43fc8000000 */
    1.07321789251584000000e+14,  /* 0x42d866f348000000 */
    2.91730863685632000000e+14,  /* 0x42f0953e28000000 */
    7.93006722514944000000e+14,  /* 0x430689e220000000 */
    2.15561576592179200000e+15}; /* 0x431ea215a0000000 */

  static const double cosh_tail[37] = {
    0.00000000000000000000e+00,  /* 0x0000000000000000 */
    6.89700037027478056904e-09,  /* 0x3e3d9f5504c2bd28 */
    4.43207835591715833630e-08,  /* 0x3e67cb66f0a4c9fd */
    2.33540217013828929694e-07,  /* 0x3e8f58617928e588 */
    5.17452463948269748331e-08,  /* 0x3e6bc7d000c38d48 */
    9.38728274131605919153e-07,  /* 0x3eaf7f9d4e329998 */
    2.73012191010840495544e-06,  /* 0x3ec6e6e464885269 */
    3.29486051438996307950e-06,  /* 0x3ecba3a8b946c154 */
    4.75803746362771416375e-06,  /* 0x3ed3f4e76110d5a4 */
    3.33050940471947692369e-05,  /* 0x3f017622515a3e2b */
    9.94707313972136215365e-06,  /* 0x3ee4dc4b528af3d0 */
    6.51685096227860253398e-05,  /* 0x3f11156278615e10 */
    1.18132406658066663359e-03,  /* 0x3f535ad50ed821f5 */
    6.93090416366541877541e-04,  /* 0x3f46b61055f2935c */
    1.45780415323416845386e-03,  /* 0x3f57e2794a601240 */
    2.99862082708111758744e-02,  /* 0x3f9eb4b45f6aadd3 */
    1.02539925859688602072e-02,  /* 0x3f85000b967b3698 */
    1.26787669807076286421e-01,  /* 0x3fc03a940fadc092 */
    6.86652631843830962843e-02,  /* 0x3fb1940bf3bf874c */
    4.81593633223853068159e-01,  /* 0x3fded26e1a2a2110 */
    1.70489514001513020602e+00,  /* 0x3ffb4740205796d6 */
    1.12416073489841270572e+01,  /* 0x40267bb3f55cb85d */
    7.06579578098005001152e+00,  /* 0x401c435ff81e18ac */
    5.91244513000686140458e+01,  /* 0x404d8fee052bdea4 */
    1.68921736147088438429e+02,  /* 0x40651d7edccde926 */
    2.60692936262087528121e+02,  /* 0x40704b1644557e0e */
    3.62419382134890611269e+02,  /* 0x4076a6b5ca0a9e1c */
    4.07689930834187453002e+03,  /* 0x40afd9cc72249abe */
    1.55377375868385224749e+04,  /* 0x40ce58de693edab5 */
    2.53720210371943103382e+04,  /* 0x40d8c70158ac6364 */
    4.78822310734952334315e+04,  /* 0x40e7614764f43e20 */
    1.81871712615542812273e+05,  /* 0x4106337db36fc718 */
    5.62892347580489004031e+05,  /* 0x41212d98b1f611e2 */
    6.41374032312148716301e+05,  /* 0x412392bc108b37cc */
    7.57809544070145115256e+06,  /* 0x415ce87bdc3473dc */
    3.64177136406482197344e+06,  /* 0x414bc8d5ae99ad14 */
    7.63580561355670914054e+06}; /* 0x415d20d76744835c */

  unsigned long ux, aux, xneg;
  double y, z, z1, z2;
  int m;

  /* Special cases */

  GET_BITS_DP64(x, ux);
  aux = ux & ~SIGNBIT_DP64;
  if (aux < 0x3e30000000000000) /* |x| small enough that sinh(x) = x */
    {
      if (aux == 0)
        /* with no inexact */
        return x;
      else
        return val_with_flags(x, AMD_F_INEXACT);
    }
  else if (aux >= 0x7ff0000000000000) /* |x| is NaN or Inf */
    {
    return x + x;
    }


  xneg = (aux != ux);

  y = x;
  if (xneg) y = -x;

  if (y >= max_sinh_arg)
    {
    /* Return +/-infinity with overflow flag */
    return retval_errno_erange(x, xneg);
    }
  else if (y >= small_threshold)
    {
      /* In this range y is large enough so that
         the negative exponential is negligible,
         so sinh(y) is approximated by sign(x)*exp(y)/2. The
         code below is an inlined version of that from
         exp() with two changes (it operates on
         y instead of x, and the division by 2 is
         done by reducing m by 1). */

      splitexp(y, 1.0, thirtytwo_by_log2, log2_by_32_lead,
               log2_by_32_tail, &m, &z1, &z2);
      m -= 1;

      if (m >= EMIN_DP64 && m <= EMAX_DP64)
        z = scaleDouble_1((z1+z2),m);
      else
        z = scaleDouble_2((z1+z2),m);
    }
  else
    {
      /* In this range we find the integer part y0 of y 
         and the increment dy = y - y0. We then compute
 
         z = sinh(y) = sinh(y0)cosh(dy) + cosh(y0)sinh(dy)

         where sinh(y0) and cosh(y0) are tabulated above. */

      int ind;
      double dy, dy2, sdy, cdy, sdy1, sdy2;

      ind = (int)y;
      dy = y - ind;

      dy2 = dy*dy;
      sdy = dy*dy2*(0.166666666666666667013899e0 +
                    (0.833333333333329931873097e-2 +
                     (0.198412698413242405162014e-3 +
                      (0.275573191913636406057211e-5 +
                       (0.250521176994133472333666e-7 +
                        (0.160576793121939886190847e-9 +
                         0.7746188980094184251527126e-12*dy2)*dy2)*dy2)*dy2)*dy2)*dy2);

      cdy = dy2*(0.500000000000000005911074e0 +
                 (0.416666666666660876512776e-1 +
                  (0.138888888889814854814536e-2 +
                   (0.248015872460622433115785e-4 +
                    (0.275573350756016588011357e-6 +
                     (0.208744349831471353536305e-8 +
                      0.1163921388172173692062032e-10*dy2)*dy2)*dy2)*dy2)*dy2)*dy2);

      /* At this point sinh(dy) is approximated by dy + sdy.
	 Shift some significant bits from dy to sdy. */

      GET_BITS_DP64(dy, ux);
      ux &= 0xfffffffff8000000;
      PUT_BITS_DP64(ux, sdy1);
      sdy2 = sdy + (dy - sdy1);

      z = ((((((cosh_tail[ind]*sdy2 + sinh_tail[ind]*cdy) 
	       + cosh_tail[ind]*sdy1) + sinh_tail[ind])  
	     + cosh_lead[ind]*sdy2) + sinh_lead[ind]*cdy) 
	   + cosh_lead[ind]*sdy1) + sinh_lead[ind];
    }

  if (xneg) z = - z;
  return z;
}
示例#4
0
文件: asinh.c 项目: sharugupta/OpenUH
double FN_PROTOTYPE(asinh)(double x)
{

  unsigned long long ux, ax, xneg;
  double absx, r, rarg, t, r1, r2, poly, s, v1, v2;
  int xexp;

  static const unsigned long long
    rteps = 0x3e46a09e667f3bcd,    /* sqrt(eps) = 1.05367121277235086670e-08 */
    recrteps = 0x4196a09e667f3bcd; /* 1/rteps = 9.49062656242515593767e+07 */

  /* log2_lead and log2_tail sum to an extra-precise version
     of log(2) */
  static const double
    log2_lead = 6.93147122859954833984e-01,  /* 0x3fe62e42e0000000 */
    log2_tail = 5.76999904754328540596e-08;  /* 0x3e6efa39ef35793c */


  GET_BITS_DP64(x, ux);
  ax = ux & ~SIGNBIT_DP64;
  xneg = ux & SIGNBIT_DP64;
  PUT_BITS_DP64(ax, absx);

  if ((ux & EXPBITS_DP64) == EXPBITS_DP64)
    {
      /* x is either NaN or infinity */
      if (ux & MANTBITS_DP64)
        {
          /* x is NaN */
#ifdef WINDOWS
          return handle_error(_FUNCNAME, ux|0x0008000000000000, _DOMAIN,
                              AMD_F_INVALID, EDOM, x, 0.0);
#else
          return x + x; /* Raise invalid if it is a signalling NaN */
#endif
        }
      else
        {
          /* x is infinity. Return the same infinity. */
#ifdef WINDOWS
          if (ux & SIGNBIT_DP64)
            return handle_error(_FUNCNAME, NINFBITPATT_DP64, _DOMAIN,
                                AMD_F_INVALID, EDOM, x, 0.0);
          else
            return handle_error(_FUNCNAME, PINFBITPATT_DP64, _DOMAIN,
                                AMD_F_INVALID, EDOM, x, 0.0);
#else
          return x;
#endif
        }
    }
  else if (ax < rteps) /* abs(x) < sqrt(epsilon) */
    {
      if (ax == 0x0000000000000000)
        {
          /* x is +/-zero. Return the same zero. */
          return x;
        }
      else
        {
          /* Tiny arguments approximated by asinh(x) = x
             - avoid slow operations on denormalized numbers */
          return val_with_flags(x,AMD_F_INEXACT);
        }
    }


  if (ax <= 0x3ff0000000000000) /* abs(x) <= 1.0 */
    {
      /* Arguments less than 1.0 in magnitude are
         approximated by [4,4] or [5,4] minimax polynomials
         fitted to asinh series 4.6.31 (x < 1) from Abramowitz and Stegun
      */
      t = x*x;
      if (ax < 0x3fd0000000000000)
        {
          /* [4,4] for 0 < abs(x) < 0.25 */
          poly =
            (-0.12845379283524906084997e0 +
             (-0.21060688498409799700819e0 +
              (-0.10188951822578188309186e0 +
               (-0.13891765817243625541799e-1 -
                0.10324604871728082428024e-3 * t) * t) * t) * t) /
            (0.77072275701149440164511e0 +
             (0.16104665505597338100747e1 +
              (0.11296034614816689554875e1 +
               (0.30079351943799465092429e0 +
                0.235224464765951442265117e-1 * t) * t) * t) * t);
        }
      else if (ax < 0x3fe0000000000000)
        {
          /* [4,4] for 0.25 <= abs(x) < 0.5 */
          poly =
            (-0.12186605129448852495563e0 +
             (-0.19777978436593069928318e0 +
              (-0.94379072395062374824320e-1 +
               (-0.12620141363821680162036e-1 -
                0.903396794842691998748349e-4 * t) * t) * t) * t) /
            (0.73119630776696495279434e0 +
             (0.15157170446881616648338e1 +
              (0.10524909506981282725413e1 +
               (0.27663713103600182193817e0 +
                0.21263492900663656707646e-1 * t) * t) * t) * t);
        }
      else if (ax < 0x3fe8000000000000)
        {
          /* [4,4] for 0.5 <= abs(x) < 0.75 */
          poly =
            (-0.81210026327726247622500e-1 +
             (-0.12327355080668808750232e0 +
              (-0.53704925162784720405664e-1 +
               (-0.63106739048128554465450e-2 -
                0.35326896180771371053534e-4 * t) * t) * t) * t) /
            (0.48726015805581794231182e0 +
             (0.95890837357081041150936e0 +
              (0.62322223426940387752480e0 +
               (0.15028684818508081155141e0 +
                0.10302171620320141529445e-1 * t) * t) * t) * t);
        }
      else
        {
          /* [5,4] for 0.75 <= abs(x) <= 1.0 */
          poly =
            (-0.4638179204422665073e-1 +
             (-0.7162729496035415183e-1 +
              (-0.3247795155696775148e-1 +
               (-0.4225785421291932164e-2 +
                (-0.3808984717603160127e-4 +
                 0.8023464184964125826e-6 * t) * t) * t) * t) * t) /
            (0.2782907534642231184e0 +
             (0.5549945896829343308e0 +
              (0.3700732511330698879e0 +
               (0.9395783438240780722e-1 +
                0.7200057974217143034e-2 * t) * t) * t) * t);
        }
      return x + x*t*poly;
    }
  else if (ax < 0x4040000000000000)
    {
      /* 1.0 <= abs(x) <= 32.0 */
      /* Arguments in this region are approximated by various
         minimax polynomials fitted to asinh series 4.6.31
         in Abramowitz and Stegun.
      */
      t = x*x;
      if (ax >= 0x4020000000000000)
        {
          /* [3,3] for 8.0 <= abs(x) <= 32.0 */
          poly =
            (-0.538003743384069117e-10 +
             (-0.273698654196756169e-9 +
              (-0.268129826956403568e-9 -
               0.804163374628432850e-29 * t) * t) * t) /
            (0.238083376363471960e-9 +
             (0.203579344621125934e-8 +
              (0.450836980450693209e-8 +
               0.286005148753497156e-8 * t) * t) * t);
        }
      else if (ax >= 0x4010000000000000)
        {
          /* [4,3] for 4.0 <= abs(x) <= 8.0 */
          poly =
            (-0.178284193496441400e-6 +
             (-0.928734186616614974e-6 +
              (-0.923318925566302615e-6 +
               (-0.776417026702577552e-19 +
                0.290845644810826014e-21 * t) * t) * t) * t) /
            (0.786694697277890964e-6 +
             (0.685435665630965488e-5 +
              (0.153780175436788329e-4 +
               0.984873520613417917e-5 * t) * t) * t);

        }
      else if (ax >= 0x4000000000000000)
        {
          /* [5,4] for 2.0 <= abs(x) <= 4.0 */
          poly =
            (-0.209689451648100728e-6 +
             (-0.219252358028695992e-5 +
              (-0.551641756327550939e-5 +
               (-0.382300259826830258e-5 +
                (-0.421182121910667329e-17 +
                 0.492236019998237684e-19 * t) * t) * t) * t) * t) /
            (0.889178444424237735e-6 +
             (0.131152171690011152e-4 +
              (0.537955850185616847e-4 +
               (0.814966175170941864e-4 +
                0.407786943832260752e-4 * t) * t) * t) * t);
        }
      else if (ax >= 0x3ff8000000000000)
        {
          /* [5,4] for 1.5 <= abs(x) <= 2.0 */
          poly =
            (-0.195436610112717345e-4 +
             (-0.233315515113382977e-3 +
              (-0.645380957611087587e-3 +
               (-0.478948863920281252e-3 +
                (-0.805234112224091742e-12 +
                 0.246428598194879283e-13 * t) * t) * t) * t) * t) /
            (0.822166621698664729e-4 +
             (0.135346265620413852e-2 +
              (0.602739242861830658e-2 +
               (0.972227795510722956e-2 +
                0.510878800983771167e-2 * t) * t) * t) * t);
        }
      else
        {
          /* [5,5] for 1.0 <= abs(x) <= 1.5 */
          poly =
            (-0.121224194072430701e-4 +
             (-0.273145455834305218e-3 +
              (-0.152866982560895737e-2 +
               (-0.292231744584913045e-2 +
                (-0.174670900236060220e-2 -
                 0.891754209521081538e-12 * t) * t) * t) * t) * t) /
            (0.499426632161317606e-4 +
             (0.139591210395547054e-2 +
              (0.107665231109108629e-1 +
               (0.325809818749873406e-1 +
                (0.415222526655158363e-1 +
                 0.186315628774716763e-1 * t) * t) * t) * t) * t);
        }
      log_kernel_amd64(absx, ax, &xexp, &r1, &r2);
      r1 = ((xexp+1) * log2_lead + r1);
      r2 = ((xexp+1) * log2_tail + r2);
      /* Now (r1,r2) sum to log(2x). Add the term
         1/(2.2.x^2) = 0.25/t, and add poly/t, carefully
         to maintain precision. (Note that we add poly/t
         rather than poly because of the *x factor used
         when generating the minimax polynomial) */
      v2 = (poly+0.25)/t;
      r = v2 + r1;
      s = ((r1 - r) + v2) + r2;
      v1 = r + s;
      v2 = (r - v1) + s;
      r = v1 + v2;
      if (xneg)
        return -r;
      else
        return r;
    }
  else
    {
      /* abs(x) > 32.0 */
      if (ax > recrteps)
        {
          /* Arguments greater than 1/sqrt(epsilon) in magnitude are
             approximated by asinh(x) = ln(2) + ln(abs(x)), with sign of x */
          /* log_kernel_amd(x) returns xexp, r1, r2 such that
             log(x) = xexp*log(2) + r1 + r2 */
          log_kernel_amd64(absx, ax, &xexp, &r1, &r2);
          /* Add (xexp+1) * log(2) to z1,z2 to get the result asinh(x).
             The computed r1 is not subject to rounding error because
             (xexp+1) has at most 10 significant bits, log(2) has 24 significant
             bits, and r1 has up to 24 bits; and the exponents of r1
             and r2 differ by at most 6. */
          r1 = ((xexp+1) * log2_lead + r1);
          r2 = ((xexp+1) * log2_tail + r2);
          if (xneg)
            return -(r1 + r2);
          else
            return r1 + r2;
        }
      else
        {
          rarg = absx*absx+1.0;
          /* Arguments such that 32.0 <= abs(x) <= 1/sqrt(epsilon) are
             approximated by
               asinh(x) = ln(abs(x) + sqrt(x*x+1))
             with the sign of x (see Abramowitz and Stegun 4.6.20) */
          /* Use assembly instruction to compute r = sqrt(rarg); */
          ASMSQRT(rarg,r);
          r += absx;
          GET_BITS_DP64(r, ax);
          log_kernel_amd64(r, ax, &xexp, &r1, &r2);
          r1 = (xexp * log2_lead + r1);
          r2 = (xexp * log2_tail + r2);
          if (xneg)
            return -(r1 + r2);
          else
            return r1 + r2;
        }
    }
}