This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
quadmath interfaces and constants
[perl5.git] / ext / POSIX / POSIX.xs
index 7bb3d65..dcda631 100644 (file)
 #include <unistd.h>
 #endif
 
-#ifndef M_E
-#  define M_E          2.71828182845904523536028747135266250
-#endif
-#ifndef M_LOG2E
-#  define M_LOG2E      1.44269504088896340735992468100189214
-#endif
-#ifndef M_LOG10E
-#  define M_LOG10E     0.434294481903251827651128918916605082
-#endif
-#ifndef M_LN2
-#  define M_LN2                0.693147180559945309417232121458176568
-#endif
-#ifndef M_LN10
-#  define M_LN10       2.30258509299404568401799145468436421
-#endif
-#ifndef M_PI
-#  define M_PI         3.14159265358979323846264338327950288
-#endif
-#ifndef M_PI_2
-#  define M_PI_2       1.57079632679489661923132169163975144
-#endif
-#ifndef M_PI_4
-#  define M_PI_4       0.785398163397448309615660845819875721
-#endif
-#ifndef M_1_PI
-#  define M_1_PI       0.318309886183790671537767526745028724
+#if defined(USE_QUADMATH) && defined(I_QUADMATH)
+
+#  undef M_E
+#  undef M_LOG2E
+#  undef M_LOG10E
+#  undef M_LN2
+#  undef M_LN10
+#  undef M_PI
+#  undef M_PI_2
+#  undef M_PI_4
+#  undef M_1_PI
+#  undef M_2_PI
+#  undef M_2_SQRTPI
+#  undef M_SQRT2
+#  undef M_SQRT1_2
+
+#  define M_E        M_Eq
+#  define M_LOG2E    M_LOG2Eq
+#  define M_LOG10E   M_LOG10Eq
+#  define M_LN2      M_LN2q
+#  define M_LN10     M_LN10q
+#  define M_PI       M_PIq
+#  define M_PI_2     M_PI_2q
+#  define M_PI_4     M_PI_4q
+#  define M_1_PI     M_1_PIq
+#  define M_2_PI     M_2_PIq
+#  define M_2_SQRTPI M_2_SQRTPIq
+#  define M_SQRT2    M_SQRT2q
+#  define M_SQRT1_2  M_SQRT1_2q
+
+#else
+
+#  ifndef M_E
+#    define M_E                2.71828182845904523536028747135266250
+#  endif
+#  ifndef M_LOG2E
+#    define M_LOG2E    1.44269504088896340735992468100189214
+#  endif
+#  ifndef M_LOG10E
+#    define M_LOG10E   0.434294481903251827651128918916605082
+#  endif
+#  ifndef M_LN2
+#    define M_LN2      0.693147180559945309417232121458176568
+#  endif
+#  ifndef M_LN10
+#    define M_LN10     2.30258509299404568401799145468436421
+#  endif
+#  ifndef M_PI
+#    define M_PI       3.14159265358979323846264338327950288
+#  endif
+#  ifndef M_PI_2
+#    define M_PI_2     1.57079632679489661923132169163975144
+#  endif
+#  ifndef M_PI_4
+#    define M_PI_4     0.785398163397448309615660845819875721
+#  endif
+#  ifndef M_1_PI
+#    define M_1_PI     0.318309886183790671537767526745028724
+#  endif
+#  ifndef M_2_PI
+#    define M_2_PI     0.636619772367581343075535053490057448
+#  endif
+#  ifndef M_2_SQRTPI
+#    define M_2_SQRTPI 1.12837916709551257389615890312154517
+#  endif
+#  ifndef M_SQRT2
+#    define M_SQRT2    1.41421356237309504880168872420969808
+#  endif
+#  ifndef M_SQRT1_2
+#    define M_SQRT1_2  0.707106781186547524400844362104849039
+#  endif
+
 #endif
-#ifndef M_2_PI
-#  define M_2_PI       0.636619772367581343075535053490057448
+
+#if !defined(INFINITY) && defined(NV_INF)
+#  define INFINITY NV_INF
 #endif
-#ifndef M_2_SQRTPI
-#  define M_2_SQRTPI   1.12837916709551257389615890312154517
+
+#if !defined(NAN) && defined(NV_NAN)
+#  define NAN NV_NAN
 #endif
-#ifndef M_SQRT2
-#  define M_SQRT2      1.41421356237309504880168872420969808
+
+#if !defined(Inf) && defined(NV_INF)
+#  define Inf NV_INF
 #endif
-#ifndef M_SQRT1_2
-#  define M_SQRT1_2    0.707106781186547524400844362104849039
+
+#if !defined(NaN) && defined(NV_NAN)
+#  define NaN NV_NAN
 #endif
 
 /* We will have an emulation. */
-#if !defined(HAS_FPCLASSIFY) && !defined(FP_INFINITE)
+#ifndef FP_INFINITE
 #  define FP_INFINITE  0
 #  define FP_NAN       1
 #  define FP_NORMAL    2
 #  define FP_ZERO      4
 #endif
 
+/* We will have an emulation. */
+#ifndef FE_TONEAREST
+#  define FE_TONEAREST 0
+#  define FE_TOWARDZERO        1
+#  define FE_DOWNWARD  2
+#  define FE_UPWARD    3
+#endif
+
 /* C89 math.h:
 
    acos asin atan atan2 ceil cos cosh exp fabs floor fmod frexp ldexp
 
    atan2 cos exp log pow sin sqrt
 
 * Berkeley/SVID extensions:
* C99 math.h added:
 
-    j0 j1 jn y0 y1 yn
+   acosh asinh atanh cbrt copysign erf erfc exp2 expm1 fdim fma fmax
+   fmin fpclassify hypot ilogb isfinite isgreater isgreaterequal isinf
+   isless islessequal islessgreater isnan isnormal isunordered lgamma
+   log1p log2 logb lrint lround nan nearbyint nextafter nexttoward remainder
+   remquo rint round scalbn signbit tgamma trunc
 
- * C99 math.h added:
+   See:
+   http://pubs.opengroup.org/onlinepubs/009695399/basedefs/math.h.html
+
+ * Berkeley/SVID extensions:
+
+   j0 j1 jn y0 y1 yn
 
-   acosh asinh atanh cbrt copysign cosh erf erfc exp2 expm1 fdim fma
-   fmax fmin fpclassify hypot ilogb isfinite isgreater isgreaterequal
-   isinf isless islessequal islessgreater isnan isnormal isunordered
-   lgamma log1p log2 logb lrint nan nearbyint nextafter nexttoward remainder
-   remquo rint round scalbn signbit sinh tanh tgamma trunc
+ * Configure already (5.21.0) scans for:
 
-  * Configure already (5.21.0) scans for:
+   fpclassify isfinite isinf isnan ilogb*l* signbit
 
-    fpclassify isfinite isinf isnan ilogb*l* signbit
+ * For floating-point round mode (which matters for e.g. lrint and rint)
+
+   fegetround fesetround
 
 */
 
+/* XXX Constant FP_FAST_FMA (if true, FMA is faster) */
+
 /* XXX Add ldiv(), lldiv()?  It's C99, but from stdlib.h, not math.h  */
 
 /* XXX Beware old gamma() -- one cannot know whether that is the
-   gamma or the log of gamma, that's why the new tgamma and lgamma. */
+ * gamma or the log of gamma, that's why the new tgamma and lgamma.
+ * Though also remember tgamma_r and lgamma_r. */
 
 /* XXX The truthiness of acosh() is the canary for all of the
  * C99 math.  This is very likely wrong, especially in non-UNIX lands
- * like Win32 and VMS, but also older UNIXes have issues.  For Win32
- * we later do some undefines for these interfaces.
+ * like Win32 and VMS, but also older UNIXes have issues.  For Win32,
+ * and other non-fully-C99, we later do some undefines for these interfaces.
  *
  * But we are very trying very hard to avoid introducing separate Configure
  * symbols for all the 40-ish new math symbols.  Especially since the set
  * of missing functions doesn't seem to follow any patterns. */
 
 #ifdef HAS_ACOSH
-#  if defined(USE_LONG_DOUBLE) && defined(HAS_ILOGBL)
+
+/* Certain AIX releases have the C99 math, but not in long double.
+ * The <math.h> has them, e.g. __expl128, but no library has them!
+ *
+ * See the comments in hints/aix.sh about long doubles.
+ *
+ * AIX 5 releases before 5.3 unknown, AIX releases 7 unknown */
+#  if defined(_AIX53) || defined(_AIX61)
+#    define NO_C99_LONG_DOUBLE_MATH
+#  endif
+
+#  if defined(USE_QUADMATH) && defined(I_QUADMATH)
+#    define c99_acosh  acoshq
+#    define c99_asinh  asinhq
+#    define c99_atanh  atanhq
+#    define c99_cbrt   cbrtq
+#    define c99_copysign       copysignq
+#    define c99_erf    erfq
+#    define c99_erfc   erfcq
+/* no exp2q */
+#    define c99_expm1  expm1q
+#    define c99_fdim   fdimq
+#    define c99_fma    fmaq
+#    define c99_fmax   fmaxq
+#    define c99_fmin   fminq
+#    define c99_hypot  hypotq
+#    define c99_ilogb  ilogbq
+#    define c99_lgamma lgammaq
+#    define c99_log1p  log1pq
+#    define c99_log2   log2q
+/* no logbq */
+/* no llrintq */
+/* no llroundq */
+#    define c99_lrint  lrintq
+#    define c99_lround lroundq
+#    define c99_nan    nanq
+#    define c99_nearbyint      nearbyintq
+#    define c99_nextafter      nextafterq
+/* no nexttowardq */
+#    define c99_remainder      remainderq
+#    define c99_remquo remquoq
+#    define c99_rint   rintq
+#    define c99_round  roundq
+#    define c99_scalbn scalbnq
+#    define c99_signbit        signbitq
+#    define c99_tgamma tgammal
+#    define c99_trunc  truncq
+#    define bessel_j0 j0q
+#    define bessel_j1 j1q
+#    define bessel_jn jnq
+#    define bessel_y0 y0q
+#    define bessel_y1 y1q
+#    define bessel_yn ynq
+#  elif defined(USE_LONG_DOUBLE) && \
+     !defined(NO_C99_LONG_DOUBLE_MATH) && \
+      defined(HAS_ILOGBL)
 /* There's already a symbol for ilogbl, we will use its truthiness
  * as the canary for all the *l variants being defined. */
 #    define c99_acosh  acoshl
 #    define c99_fmin   fminl
 #    define c99_hypot  hypotl
 #    define c99_ilogb  ilogbl
-#    define c99_lgamma gammal
+#    define c99_lgamma lgammal
 #    define c99_log1p  log1pl
 #    define c99_log2   log2l
 #    define c99_logb   logbl
 #    else
 #      define c99_lrint        lrintl
 #    endif
+#    if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG
+#      define c99_lround       llroundl
+#    else
+#      define c99_lround       lroundl
+#    endif
 #    define c99_nan    nanl
 #    define c99_nearbyint      nearbyintl
 #    define c99_nextafter      nextafterl
 #    else
 #      define c99_lrint        lrint
 #    endif
+#    if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG
+#      define c99_lround       llround
+#    else
+#      define c99_lround       lround
+#    endif
 #    define c99_nan    nan
 #    define c99_nearbyint      nearbyint
 #    define c99_nextafter      nextafter
 #    define c99_trunc  trunc
 #  endif
 
+#  ifndef isunordered
+#    ifdef Perl_isnan
+#      define isunordered(x, y) (Perl_isnan(x) || Perl_isnan(y))
+#    elif defined(HAS_UNORDERED)
+#      define isunordered(x, y) unordered(x, y)
+#    endif
+#  endif
+
+#  if !defined(isgreater) && defined(isunordered)
+#    define isgreater(x, y)         (!isunordered((x), (y)) && (x) > (y))
+#    define isgreaterequal(x, y)    (!isunordered((x), (y)) && (x) >= (y))
+#    define isless(x, y)            (!isunordered((x), (y)) && (x) < (y))
+#    define islessequal(x, y)       (!isunordered((x), (y)) && (x) <= (y))
+#    define islessgreater(x, y)     (!isunordered((x), (y)) && \
+                                     ((x) > (y) || (y) > (x)))
+#  endif
+
 /* Check both the Configure symbol and the macro-ness (like C99 promises). */ 
 #  if defined(HAS_FPCLASSIFY) && defined(fpclassify)
 #    define c99_fpclassify     fpclassify
 /* If on legacy platforms, and not using gcc, some C99 math interfaces
  * might be missing, turn them off so that the emulations hopefully
  * kick in.  This is admittedly nasty, and fragile, but the alternative
- * is to have Configure scans for all the 40+ interfaces. */
-#ifndef __GNUC__
+ * is to have Configure scans for all the 40+ interfaces.
+ *
+ * For some platforms, also the gcc implementations are missing
+ * certain interfaces.
+ *
+ * In other words: if you have an incomplete (or broken) C99 math interface,
+ * #undef the c99_foo here, and let the emulations kick in. */
 
-/* HP-UX on PA-RISC is missing certain C99 math functions,
- * but on IA64 (Integrity) these do exist. */
-#  if defined(__hpux) && defined(__hppa)
-#    undef c99_fma
+#ifdef __GNUC__
+
+/* using gcc */
+
+#  if defined(__hpux) && (defined(__hppa) || defined(_PA_RISC))
+#      undef c99_nexttoward
+#      undef c99_tgamma
+#  endif
+
+#else
+
+/* not using gcc */
+
+#  if defined(_AIX53) || defined(_AIX61) /* AIX 7 has nexttoward */
 #    undef c99_nexttoward
-#    undef c99_tgamma
+#  endif
+
+/* HP-UX on PA-RISC is missing certain C99 math functions,
+ * but on IA64 (Integrity) these do exist, and even on
+ * recent enough HP-UX (cc) releases. */
+#  if defined(__hpux) && (defined(__hppa) || defined(_PA_RISC))
+/* lowest known release, could be lower */
+#    if defined(__HP_cc) && __HP_cc >= 111120
+#      undef c99_fma
+#      undef c99_nexttoward
+#      undef c99_tgamma
+#    else
+#      undef c99_exp2
+#      undef c99_fdim
+#      undef c99_fma
+#      undef c99_fmax
+#      undef c99_fmin
+#      undef c99_fpclassify /* hpux 10.20 has fpclassify but different api */
+#      undef c99_lrint
+#      undef c99_lround
+#      undef c99_nan
+#      undef c99_nearbyint
+#      undef c99_nexttoward
+#      undef c99_remquo
+#      undef c99_round
+#      undef c99_scalbn
+#      undef c99_tgamma
+#      undef c99_trunc
+#    endif
 #  endif
 
 #  if defined(__irix__)
 #    undef c99_isinf
 #    undef c99_isunordered
 #    undef c99_lrint
+#    undef c99_lround
 #    undef c99_nearbyint
 #    undef c99_nexttoward
 #    undef c99_remquo
-#    undef c99_rint
 #    undef c99_round
 #    undef c99_scalbn
 #  endif
 
 #endif
 
+/* XXX Regarding C99 math.h, VMS seems to be missing these:
+
+  lround nan nearbyint round scalbn llrint
+ */
+
+#ifdef __VMS
+#    undef c99_lround
+#    undef c99_nan
+#    undef c99_nearbyint
+#    undef c99_round
+#    undef c99_scalbn
+/* Have lrint but not llrint. */
+#    if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG
+#      undef c99_lrint
+#    endif
+#endif
+
 /* XXX Regarding C99 math.h, Win32 seems to be missing these:
 
-  exp2 fdim fma fmax fmin fpclassify ilogb lgamma log1p log2 lrint
+  erf erfc exp2 fdim fma fmax fmin fpclassify ilogb lgamma log1p log2 lrint
   remquo rint signbit tgamma trunc
 
   Win32 does seem to have these:
 
-  acosh asinh atanh cbrt copysign cosh erf erfc expm1 hypot log10 nan
+  acosh asinh atanh cbrt copysign cosh expm1 hypot log10 nan
   nearbyint nextafter nexttoward remainder round scalbn
 
   And the Bessel functions are defined like _this.
 */
 
 #ifdef WIN32
+#  undef c99_erf
+#  undef c99_erfc
 #  undef c99_exp2
 #  undef c99_fdim
 #  undef c99_fma
 #  undef c99_log1p
 #  undef c99_log2
 #  undef c99_lrint
+#  undef c99_lround
 #  undef c99_remquo
 #  undef c99_rint
 #  undef c99_signbit
 #  define c99_logb _logb
 #  define c99_nextafter _nextafter
 
+#  define bessel_j0 _j0
+#  define bessel_j1 _j1
+#  define bessel_jn _jn
+#  define bessel_y0 _y0
+#  define bessel_y1 _y1
+#  define bessel_yn _yn
+
+#endif
+
+#ifdef __CYGWIN__
+#  undef c99_nexttoward
 #endif
 
 /* The Bessel functions: BSD, SVID, XPG4, and POSIX.  But not C99. */
-#ifdef HAS_J0
+#if defined(HAS_J0) && !defined(bessel_j0)
 #  if defined(USE_LONG_DOUBLE) && defined(HAS_J0L)
 #    define bessel_j0 j0l
 #    define bessel_j1 j1l
 #    define bessel_y1 y1l
 #    define bessel_yn ynl
 #  else
-#    ifdef WIN32
-#      define bessel_j0 _j0
-#      define bessel_j1 _j1
-#      define bessel_jn _jn
-#      define bessel_y0 _y0
-#      define bessel_y1 _y1
-#      define bessel_yn _yn
-#    else
-#      define bessel_j0 j0
-#      define bessel_j1 j1
-#      define bessel_jn jn
-#      define bessel_y0 y0
-#      define bessel_y1 y1
-#      define bessel_yn yn
-#    endif
+#    define bessel_j0 j0
+#    define bessel_j1 j1
+#    define bessel_jn jn
+#    define bessel_y0 y0
+#    define bessel_y1 y1
+#    define bessel_yn yn
 #  endif
 #endif
 
  * See e.g. http://www.johndcook.com/math_h.html
  */
 
-/* XXX cosh sinh tanh */
-
 #ifndef c99_acosh
 static NV my_acosh(NV x)
 {
@@ -411,6 +624,39 @@ static NV my_copysign(NV x, NV y)
 #  define c99_copysign my_copysign
 #endif
 
+/* XXX cosh (though c89) */
+
+#ifndef c99_erf
+static NV my_erf(NV x)
+{
+  /* http://www.johndcook.com/cpp_erf.html -- public domain */
+  NV a1 =  0.254829592;
+  NV a2 = -0.284496736;
+  NV a3 =  1.421413741;
+  NV a4 = -1.453152027;
+  NV a5 =  1.061405429;
+  NV p  =  0.3275911;
+  NV t, y;
+  int sign = x < 0 ? -1 : 1; /* Save the sign. */
+  x = PERL_ABS(x);
+
+  /* Abramowitz and Stegun formula 7.1.26 */
+  t = 1.0 / (1.0 + p * x);
+  y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1) * t * Perl_exp(-x*x);
+
+  return sign * y;
+}
+#  define c99_erf my_erf
+#endif
+
+#ifndef c99_erfc
+static NV my_erfc(NV x) {
+  /* This is not necessarily numerically stable, but better than nothing. */
+  return 1.0 - c99_erf(x);
+}
+#  define c99_erfc my_erfc
+#endif
+
 #ifndef c99_exp2
 static NV my_exp2(NV x)
 {
@@ -423,8 +669,10 @@ static NV my_exp2(NV x)
 static NV my_expm1(NV x)
 {
   if (PERL_ABS(x) < 1e-5)
+    /* http://www.johndcook.com/cpp_expm1.html -- public domain.
+     * Taylor series, the first four terms (the last term quartic). */
     /* Probably not enough for long doubles. */
-    return x * (1.0 + x * (0.5 + x / 6.0)); /* Taylor series */
+    return x * (1.0 + x * (1/2.0 + x * (1/6.0 + x/24.0)));
   else
     return Perl_exp(x) - 1;
 }
@@ -465,73 +713,41 @@ static NV my_fmin(NV x, NV y)
 #  define c99_fmin my_fmin
 #endif
 
-#ifndef HAS_FPCLASSIFY
-static NV my_fpclassify(NV x)
+#ifndef c99_fpclassify
+
+static IV my_fpclassify(NV x)
 {
-#if (defined(HAS_FPCLASS) || defined(HAS_FPCLASSL)) && defined(FP_CLASS_SNAN)
-  switch (Perl_fp_class(x)) {
-  case FP_CLASS_NINF:    case FP_CLASS_PINF:    return FP_INFINITE;
-  case FP_CLASS_SNAN:    case FP_CLASS_QNAN:    return FP_NAN;
-  case FP_CLASS_NNORM:   case FP_CLASS_PNORM:   return FP_NORMAL;
-  case FP_CLASS_NDENORM: case FP_CLASS_PDENORM: return FP_SUBNORMAL;
-  case FP_CLASS_NZERO:   case FP_CLASS_PZERO:   return FP_ZERO;
-  default: return -1;
-  }
-#  define c99_fpclassify my_fpclassify
-#elif (defined(HAS_FPCLASS) || defined(HAS_FP_CLASSL)) && defined(FP_SNAN)
-  switch (Perl_fp_class(x)) {
-  case FP_NINF:    case FP_PINF:    return FP_INFINITE;
-  case FP_SNAN:    case FP_QNAN:    return FP_NAN;
-  case FP_NNORM:   case FP_PNORM:   return FP_NORMAL;
-  case FP_NDENORM: case FP_PDENORM: return FP_SUBNORMAL;
-  case FP_NZERO:   case FP_PZERO:   return FP_ZERO;
-  default: return -1;
-  }
-#  define c99_fpclassify my_fpclassify
-#elif defined(HAS_FP_CLASS) && defined(FP_POS_INF)
-  switch (Perl_fp_class(x)) {
-  case FP_NEG_INF:    case FP_POS_INF:    return FP_INFINITE;
-  case FP_SNAN:       case FP_QNAN:       return FP_NAN;
-  case FP_NEG_NORM:   case FP_POS_NORM:   return FP_NORMAL;
-  case FP_NEG_DENORM: case FP_POS_DENORM: return FP_SUBNORMAL;
-  case FP_NEG_ZERO:   case FP_POS_ZERO:   return FP_ZERO;
-  default: return -1;
-  }
-#  define c99_fpclassify my_fpclassify
-#elif defined(HAS_CLASS) && defined(FP_PLUS_INF)
-  switch (Perl_fp_class(x)) {
-  case FP_MINUS_INF:    case FP_PLUS_INF:    return FP_INFINITE;
-  case FP_SNAN:         case FP_QNAN:        return FP_NAN;
-  case FP_MINUS_NORM:   case FP_PLUS_NORM:   return FP_NORMAL;
-  case FP_MINUS_DENORM: case FP_PLUS_DENORM: return FP_SUBNORMAL;
-  case FP_MINUS_ZERO:   case FP_PLUS_ZERO:   return FP_ZERO;
-  default: return -1;
-  }
-#  define c99_fpclassify my_fpclassify
-#elif defined(HAS_FP_CLASSIFY)
-  return Perl_fp_class(x);
-#  define c99_fpclassify my_fpclassify
-#elif defined(WIN32)
-  int fpclass = _fpclass(x);
+#ifdef Perl_fp_class_inf
   if (Perl_fp_class_inf(x))    return FP_INFINITE;
   if (Perl_fp_class_nan(x))    return FP_NAN;
   if (Perl_fp_class_norm(x))   return FP_NORMAL;
   if (Perl_fp_class_denorm(x)) return FP_SUBNORMAL;
   if (Perl_fp_class_zero(x))   return FP_ZERO;
-  return -1;
 #  define c99_fpclassify my_fpclassify
 #endif
+  return -1;
 }
+
 #endif
 
 #ifndef c99_hypot
 static NV my_hypot(NV x, NV y)
 {
-  if (x > 0.0) {
-    NV t = y / x;
-    return PERL_ABS(x) * Perl_sqrt(1 + t * t);
+  /* http://en.wikipedia.org/wiki/Hypot */
+  NV t;
+  x = PERL_ABS(x); /* Take absolute values. */
+  if (y == 0)
+    return x;
+  if (Perl_isnan(y))
+    return NV_INF;
+  y = PERL_ABS(y);
+  if (x < y) { /* Swap so that y is less. */
+    t = x;
+    x = y;
+    y = t;
   }
-  return NV_NAN;
+  t = y / x;
+  return x * Perl_sqrt(1.0 + t * t);
 }
 #  define c99_hypot my_hypot
 #endif
@@ -544,14 +760,22 @@ static IV my_ilogb(NV x)
 #  define c99_ilogb my_ilogb
 #endif
 
+/* XXX lgamma -- non-trivial */
+
 #ifndef c99_log1p
 static NV my_log1p(NV x)
 {
+  /* http://www.johndcook.com/cpp_log_one_plus_x.html -- public domain.
+   * Taylor series, the first four terms (the last term quartic). */
+  if (x < -1.0)
+    return NV_NAN;
+  if (x == -1.0)
+    return -NV_INF;
   if (PERL_ABS(x) > 1e-4)
     return Perl_log(1.0 + x);
   else
     /* Probably not enough for long doubles. */
-    return x * (1.0 - x * (-x / 2.0 + x / 3.0)); /* Taylor series */
+    return x * (1.0 + x * (-1/2.0 + x * (1/3.0 - x/4.0)));
 }
 #  define c99_log1p my_log1p
 #endif
@@ -564,17 +788,113 @@ static NV my_log2(NV x)
 #  define c99_log2 my_log2
 #endif
 
+/* XXX nextafter */
+
+/* XXX nexttoward */
+
+static int my_fegetround()
+{
+#ifdef HAS_FEGETROUND
+  return fegetround();
+#elif defined(FLT_ROUNDS)
+  return FLT_ROUNDS;
+#elif defined(HAS_FPGETROUND)
+  switch (fpgetround()) {
+  default:
+  case FP_RN: return FE_TONEAREST;
+  case FP_RZ: return FE_TOWARDZERO;
+  case FP_RM: return FE_DOWNWARD;
+  case FE_RP: return FE_UPWARD;
+  }
+#else
+  return -1;
+#endif
+}
+
+/* Toward closest integer. */
+#define MY_ROUND_NEAREST(x) ((NV)((IV)((x) >= 0.0 ? (x) + 0.5 : (x) - 0.5)))
+
+/* Toward zero. */
+#define MY_ROUND_TRUNC(x) ((NV)((IV)(x)))
+
+/* Toward minus infinity. */
+#define MY_ROUND_DOWN(x) ((NV)((IV)((x) >= 0.0 ? (x) : (x) - 0.5)))
+
+/* Toward plus infinity. */
+#define MY_ROUND_UP(x) ((NV)((IV)((x) >= 0.0 ? (x) + 0.5 : (x))))
+
+static NV my_rint(NV x)
+{
+#ifdef FE_TONEAREST
+  switch (my_fegetround()) {
+  default:
+  case FE_TONEAREST:  return MY_ROUND_NEAREST(x);
+  case FE_TOWARDZERO: return MY_ROUND_TRUNC(x);
+  case FE_DOWNWARD:   return MY_ROUND_DOWN(x);
+  case FE_UPWARD:     return MY_ROUND_UP(x);
+  }
+#elif defined(HAS_FPGETROUND)
+  switch (fpgetround()) {
+  default:
+  case FP_RN: return MY_ROUND_NEAREST(x);
+  case FP_RZ: return MY_ROUND_TRUNC(x);
+  case FP_RM: return MY_ROUND_DOWN(x);
+  case FE_RP: return MY_ROUND_UP(x);
+  }
+#else
+  return NV_NAN;
+#endif
+}
+
+/* XXX nearbyint() and rint() are not really identical -- but the difference
+ * is messy: nearbyint is defined NOT to raise FE_INEXACT floating point
+ * exceptions, while rint() is defined to MAYBE raise them.  At the moment
+ * Perl is blissfully unaware of such fine detail of floating point. */
+#ifndef c99_nearbyint
+#  ifdef FE_TONEAREST
+#    define c99_nearbyrint my_rint
+#  endif
+#endif
+
+#ifndef c99_lrint
+#  ifdef FE_TONEAREST
+static IV my_lrint(NV x)
+{
+  return (IV)my_rint(x);
+}
+#    define c99_lrint my_lrint
+#  endif
+#endif
+
+#ifndef c99_lround
+static IV my_lround(NV x)
+{
+  return (IV)MY_ROUND_NEAREST(x);
+}
+#  define c99_lround my_lround
+#endif
+
+/* XXX remainder */
+
+/* XXX remquo */
+
+#ifndef c99_rint
+#  ifdef FE_TONEAREST
+#    define c99_rint my_rint
+#  endif
+#endif
+
 #ifndef c99_round
 static NV my_round(NV x)
 {
-  return (NV)((IV)(x >= 0.0 ? x + 0.5 : x - 0.5));
+  return MY_ROUND_NEAREST(x);
 }
 #  define c99_round my_round
 #endif
 
 #ifndef c99_scalbn
-#   if defined(Perl_ldexpl) && FLT_RADIX == 2
-static NV my_scalbn(NV x)
+#   if defined(Perl_ldexp) && FLT_RADIX == 2
+static NV my_scalbn(NV x, int y)
 {
   return Perl_ldexp(x, y);
 }
@@ -582,13 +902,7 @@ static NV my_scalbn(NV x)
 #  endif
 #endif
 
-#ifndef c99_trunc
-static NV my_trunc(NV x)
-{
-  return (NV)((IV)(x));
-}
-#  define c99_trunc my_trunc
-#endif
+/* XXX sinh (though c89) */
 
 #ifndef c99_tgamma
 #  ifdef c99_lgamma
@@ -597,10 +911,21 @@ static NV my_tgamma(NV x)
   double l = c99_lgamma(x);
   return signgam * Perl_exp(l); /* XXX evil global signgam, need lgamma_r */
 }
-#  define c99_tgamma my_tgamma
+#    define c99_tgamma my_tgamma
+/* XXX tgamma without lgamma -- non-trivial */
 #  endif
 #endif
 
+/* XXX tanh (though c89) */
+
+#ifndef c99_trunc
+static NV my_trunc(NV x)
+{
+  return MY_ROUND_TRUNC(x);
+}
+#  define c99_trunc my_trunc
+#endif
+
 /* XXX This comment is just to make I_TERMIO and I_SGTTY visible to
    metaconfig for future extension writers.  We don't use them in POSIX.
    (This is really sneaky :-)  --AD
@@ -1675,9 +2000,10 @@ acos(x)
        y0 = 29
        y1 = 30
     CODE:
+       RETVAL = NV_NAN;
        switch (ix) {
        case 0:
-           RETVAL = acos(x); /* C89 math */
+           RETVAL = Perl_acos(x); /* C89 math */
            break;
        case 1:
 #ifdef c99_acosh
@@ -1687,7 +2013,7 @@ acos(x)
 #endif
            break;
        case 2:
-           RETVAL = asin(x); /* C89 math */
+           RETVAL = Perl_asin(x); /* C89 math */
            break;
        case 3:
 #ifdef c99_asinh
@@ -1697,7 +2023,7 @@ acos(x)
 #endif
            break;
        case 4:
-           RETVAL = atan(x); /* C89 math */
+           RETVAL = Perl_atan(x); /* C89 math */
            break;
        case 5:
 #ifdef c99_atanh
@@ -1714,10 +2040,10 @@ acos(x)
 #endif
            break;
        case 7:
-           RETVAL = ceil(x); /* C89 math */
+           RETVAL = Perl_ceil(x); /* C89 math */
            break;
        case 8:
-           RETVAL = cosh(x); /* C89 math */
+           RETVAL = Perl_cosh(x); /* C89 math */
            break;
        case 9:
 #ifdef c99_erf
@@ -1728,7 +2054,7 @@ acos(x)
            break;
        case 10:
 #ifdef c99_erfc
-           RETVAL = erfc(x);
+           RETVAL = c99_erfc(x);
 #else
            not_here("erfc");
 #endif
@@ -1748,24 +2074,26 @@ acos(x)
 #endif
            break;
        case 13:
-           RETVAL = floor(x); /* C89 math */
+           RETVAL = Perl_floor(x); /* C89 math */
            break;
        case 14:
 #ifdef bessel_j0
            RETVAL = bessel_j0(x);
 #else
-           not_here("bessel_j0");
+           not_here("j0");
 #endif
            break;
        case 15:
 #ifdef bessel_j1
            RETVAL = bessel_j1(x);
 #else
-           not_here("bessel_j1");
+           not_here("j1");
 #endif
            break;
        case 16:
-        /* XXX lgamma_r */
+        /* XXX lgamma_r -- the lgamma accesses a global variable (signgam),
+         * which is evil.  Some platforms have lgamma_r, which has
+         * extra parameter instead of the global variable. */
 #ifdef c99_lgamma
            RETVAL = c99_lgamma(x);
 #else
@@ -1818,16 +2146,18 @@ acos(x)
 #endif
            break;
        case 24:
-           RETVAL = sinh(x); /* C89 math */
+           RETVAL = Perl_sinh(x); /* C89 math */
            break;
        case 25:
-           RETVAL = tan(x); /* C89 math */
+           RETVAL = Perl_tan(x); /* C89 math */
            break;
        case 26:
-           RETVAL = tanh(x); /* C89 math */
+           RETVAL = Perl_tanh(x); /* C89 math */
            break;
        case 27:
-        /* XXX tgamma_r */
+        /* XXX tgamma_r -- the lgamma accesses a global variable (signgam),
+         * which is evil.  Some platforms have tgamma_r, which has
+         * extra parameter instead of the global variable. */
 #ifdef c99_tgamma
            RETVAL = c99_tgamma(x);
 #else
@@ -1845,7 +2175,7 @@ acos(x)
 #ifdef bessel_y0
            RETVAL = bessel_y0(x);
 #else
-           not_here("bessel_y0");
+           not_here("y0");
 #endif
            break;
         case 30:
@@ -1853,7 +2183,7 @@ acos(x)
 #ifdef bessel_y1
            RETVAL = bessel_y1(x);
 #else
-           not_here("bessel_y1");
+           not_here("y1");
 #endif
        }
     OUTPUT:
@@ -1863,10 +2193,9 @@ IV
 fegetround()
     CODE:
 #ifdef HAS_FEGETROUND
-       RETVAL = fegetround();
-#elif defined(FLT_ROUNDS)
-       RETVAL = FLT_ROUNDS;
+       RETVAL = my_fegetround();
 #else
+       RETVAL = -1;
        not_here("fegetround");
 #endif
     OUTPUT:
@@ -1878,7 +2207,16 @@ fesetround(x)
     CODE:
 #ifdef HAS_FEGETROUND /* canary for fesetround */
        RETVAL = fesetround(x);
+#elif defined(HAS_FPGETROUND) /* canary for fpsetround */
+       switch (x) {
+        default:
+       case FE_TONEAREST:  RETVAL = fpsetround(FP_RN); break;
+       case FE_TOWARDZERO: RETVAL = fpsetround(FP_RZ); break;
+       case FE_DOWNWARD:   RETVAL = fpsetround(FP_RM); break;
+       case FE_UPWARD:     RETVAL = fpsetround(FP_RP); break;
+       }
 #else
+       RETVAL = -1;
        not_here("fesetround");
 #endif
     OUTPUT:
@@ -1893,8 +2231,11 @@ fpclassify(x)
        isinf = 3
        isnan = 4
        isnormal = 5
-        signbit = 6
+       lrint = 6
+       lround = 7
+        signbit = 8
     CODE:
+       RETVAL = -1;
        switch (ix) {
        case 0:
 #ifdef c99_fpclassify
@@ -1927,6 +2268,20 @@ fpclassify(x)
 #endif
            break;
        case 6:
+#ifdef c99_lrint
+           RETVAL = c99_lrint(x);
+#else
+           not_here("lrint");
+#endif
+           break;
+       case 7:
+#ifdef c99_lround
+           RETVAL = c99_lround(x);
+#else
+           not_here("lround");
+#endif
+           break;
+       case 8:
        default:
 #ifdef Perl_signbit
            RETVAL = Perl_signbit(x);
@@ -1956,6 +2311,7 @@ copysign(x,y)
        nexttoward = 13
        remainder = 14
     CODE:
+       RETVAL = NV_NAN;
        switch (ix) {
        case 0:
 #ifdef c99_copysign
@@ -1986,7 +2342,7 @@ copysign(x,y)
 #endif
            break;
        case 4:
-           RETVAL = fmod(x, y); /* C89 math */
+           RETVAL = Perl_fmod(x, y); /* C89 math */
            break;
        case 5:
 #ifdef c99_hypot
@@ -2107,6 +2463,7 @@ scalbn(x,y)
 #ifdef c99_scalbn
        RETVAL = c99_scalbn(x, y);
 #else
+       RETVAL = NV_NAN;
        not_here("scalbn");
 #endif
     OUTPUT:
@@ -2121,6 +2478,7 @@ fma(x,y,z)
 #ifdef c99_fma
        RETVAL = c99_fma(x, y, z);
 #else
+       RETVAL = NV_NAN;
        not_here("fma");
 #endif
     OUTPUT:
@@ -2131,9 +2489,12 @@ nan(s = 0)
        char*   s;
     CODE:
 #ifdef c99_nan
-       RETVAL = c99_nan(s);
+       RETVAL = c99_nan(s ? s : "");
 #else
+       RETVAL = NV_NAN;
+#  ifndef NV_NAN
        not_here("nan");
+#  endif
 #endif
     OUTPUT:
        RETVAL
@@ -2145,6 +2506,7 @@ jn(x,y)
     ALIAS:
        yn = 1
     CODE:
+       RETVAL = NV_NAN;
         switch (ix) {
        case 0:
 #ifdef bessel_jn