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 3708b3e..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
-#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
+#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
 
 #if !defined(INFINITY) && defined(NV_INF)
 #  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
    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 nan nearbyint nextafter nexttoward remainder
+   log1p log2 logb lrint lround nan nearbyint nextafter nexttoward remainder
    remquo rint round scalbn signbit tgamma trunc
 
+   See:
+   http://pubs.opengroup.org/onlinepubs/009695399/basedefs/math.h.html
+
  * Berkeley/SVID extensions:
 
    j0 j1 jn y0 y1 yn
  * 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
 #    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
 
-#  if !defined(isunordered) && defined(Perl_isnan)
-#    define isunordered(x, y) (Perl_isnan(x) || Perl-isnan(y))
-#  elsif defined(HAS_UNORDERED)
-#    define isunordered(x, y) unordered(x, y)
+#  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)
  * kick in.  This is admittedly nasty, and fragile, but the alternative
  * 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. */
 
-#ifndef __GNUC__
+#ifdef __GNUC__
 
-/* 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_exp2
-#    undef c99_fdim
-#    undef c99_fma
-#    undef c99_fmax
-#    undef c99_fmin
-#    undef c99_fpclassify
-#    undef c99_lrint
-#    undef c99_nan
-#    undef c99_nearbyint
+/* 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_remquo
-#    undef c99_round
-#    undef c99_scalbn
-#    undef c99_tgamma
-#    undef c99_trunc
+#  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
 
 /* XXX Regarding C99 math.h, VMS seems to be missing these:
 
-  nan nearbyint round scalbn llrint
+  lround nan nearbyint round scalbn llrint
  */
 
 #ifdef __VMS
+#    undef c99_lround
 #    undef c99_nan
 #    undef c99_nearbyint
 #    undef c99_round
 
 /* 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
 
 #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
@@ -489,25 +636,23 @@ static NV my_erf(NV x)
   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 */
-  NV t = 1.0 / (1.0 + p * x);
-  NV y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1) * t * exp(-x*x);
+  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
 
-/* While in theory erfc(x) is just 1-erf(x), thanks to numerical stability
- * things are not so easy. */
 #ifndef c99_erfc
 static NV my_erfc(NV x) {
-  /* Chiani, Dardari & Simon (2003), via Wikipedia */
-  return Perl_exp(-x*x) / 6.0 + Perl_exp(-4.0/3.0 * x*x) / 2.0;
+  /* This is not necessarily numerically stable, but better than nothing. */
+  return 1.0 - c99_erf(x);
 }
 #  define c99_erfc my_erfc
 #endif
@@ -525,9 +670,9 @@ static NV my_expm1(NV x)
 {
   if (PERL_ABS(x) < 1e-5)
     /* http://www.johndcook.com/cpp_expm1.html -- public domain.
-     * Also including the cubic term. */
+     * 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;
 }
@@ -572,71 +717,15 @@ static NV my_fmin(NV x, NV y)
 
 static IV my_fpclassify(NV x)
 {
-#if defined(HAS_FPCLASSIFY) && defined(FP_PLUS_INF) /* E.g. HP-UX */
-  switch (Perl_fp_class(x)) {
-  case FP_PLUS_INF:    case FP_MINUS_INF:    return FP_INFINITE;
-  case FP_SNAN:        case FP_QNAN:         return FP_NAN;
-  case FP_PLUS_NORM:   case FP_MINUS_NORM:   return FP_NORMAL;
-  case FP_PLUS_DENORM: case FP_MINUS_DENORM: return FP_SUBNORMAL;
-  case FP_PLUS_ZERO:   case FP_MINUS_ZERO:   return FP_ZERO;
-  default: return -1;
-  }
-#  define c99_fpclassify my_fpclassify
-#elif (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
-#else
-  return -1;
 #endif
+  return -1;
 }
 
 #endif
@@ -658,7 +747,7 @@ static NV my_hypot(NV x, NV y)
     y = t;
   }
   t = y / x;
-  return x * sqrt(1.0 + t * t);
+  return x * Perl_sqrt(1.0 + t * t);
 }
 #  define c99_hypot my_hypot
 #endif
@@ -677,12 +766,16 @@ static IV my_ilogb(NV x)
 static NV my_log1p(NV x)
 {
   /* http://www.johndcook.com/cpp_log_one_plus_x.html -- public domain.
-   * Including also quadratic term. */
+   * 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
@@ -705,30 +798,50 @@ static int my_fegetround()
   return fegetround();
 #elif defined(FLT_ROUNDS)
   return FLT_ROUNDS;
-  /* XXX emulate using fpgetround() (HAS_FPGETROUND):
-   * FP_RN to nearest, FP_RM down, FP_RP, up, FP_RZ truncate */
+#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 (NV)((IV)(x >= 0.0 ? x + 0.5 : x - 0.5)); /* like round() */
-  case FE_TOWARDZERO:
-    return (NV)((IV)(x)); /* like trunc() */
-  case FE_DOWNWARD:
-    return (NV)((IV)(x >= 0.0 ? x : x - 0.5));
-  case FE_UPWARD:
-    return (NV)((IV)(x >= 0.0 ? x + 0.5 : x));
+  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
-  /* XXX emulate using fpsetround() (HAS_FPGETROUND):
-   * FP_RN to nearest, FP_RM down, FP_RP, up, FP_RZ truncate */
   return NV_NAN;
 #endif
 }
@@ -745,7 +858,7 @@ static NV my_rint(NV x)
 
 #ifndef c99_lrint
 #  ifdef FE_TONEAREST
-static IV lrint(NV x)
+static IV my_lrint(NV x)
 {
   return (IV)my_rint(x);
 }
@@ -753,6 +866,14 @@ static IV lrint(NV x)
 #  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 */
@@ -766,7 +887,7 @@ static IV lrint(NV x)
 #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
@@ -800,7 +921,7 @@ static NV my_tgamma(NV x)
 #ifndef c99_trunc
 static NV my_trunc(NV x)
 {
-  return (NV)((IV)(x));
+  return MY_ROUND_TRUNC(x);
 }
 #  define c99_trunc my_trunc
 #endif
@@ -1882,7 +2003,7 @@ acos(x)
        RETVAL = NV_NAN;
        switch (ix) {
        case 0:
-           RETVAL = acos(x); /* C89 math */
+           RETVAL = Perl_acos(x); /* C89 math */
            break;
        case 1:
 #ifdef c99_acosh
@@ -1892,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
@@ -1902,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
@@ -1919,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
@@ -1933,7 +2054,7 @@ acos(x)
            break;
        case 10:
 #ifdef c99_erfc
-           RETVAL = erfc(x);
+           RETVAL = c99_erfc(x);
 #else
            not_here("erfc");
 #endif
@@ -1953,7 +2074,7 @@ acos(x)
 #endif
            break;
        case 13:
-           RETVAL = floor(x); /* C89 math */
+           RETVAL = Perl_floor(x); /* C89 math */
            break;
        case 14:
 #ifdef bessel_j0
@@ -2025,13 +2146,13 @@ 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 -- the lgamma accesses a global variable (signgam),
@@ -2086,6 +2207,14 @@ 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");
@@ -2103,7 +2232,8 @@ fpclassify(x)
        isnan = 4
        isnormal = 5
        lrint = 6
-        signbit = 7
+       lround = 7
+        signbit = 8
     CODE:
        RETVAL = -1;
        switch (ix) {
@@ -2145,6 +2275,13 @@ fpclassify(x)
 #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);
@@ -2205,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
@@ -2352,10 +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