This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
lseek fd can be bad.
[perl5.git] / ext / POSIX / POSIX.xs
index 490249e..514b514 100644 (file)
 
 #else
 
+#  ifdef USE_LONG_DOUBLE
+#    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 FLOAT_C(c) CAT2(c,L)
+#  else
+#    define FLOAT_C(c) (c)
+#  endif
+
 #  ifndef M_E
-#    define M_E                2.71828182845904523536028747135266250
+#    define M_E                FLOAT_C(2.71828182845904523536028747135266250)
 #  endif
 #  ifndef M_LOG2E
-#    define M_LOG2E    1.44269504088896340735992468100189214
+#    define M_LOG2E    FLOAT_C(1.44269504088896340735992468100189214)
 #  endif
 #  ifndef M_LOG10E
-#    define M_LOG10E   0.434294481903251827651128918916605082
+#    define M_LOG10E   FLOAT_C(0.434294481903251827651128918916605082)
 #  endif
 #  ifndef M_LN2
-#    define M_LN2      0.693147180559945309417232121458176568
+#    define M_LN2      FLOAT_C(0.693147180559945309417232121458176568)
 #  endif
 #  ifndef M_LN10
-#    define M_LN10     2.30258509299404568401799145468436421
+#    define M_LN10     FLOAT_C(2.30258509299404568401799145468436421)
 #  endif
 #  ifndef M_PI
-#    define M_PI       3.14159265358979323846264338327950288
+#    define M_PI       FLOAT_C(3.14159265358979323846264338327950288)
 #  endif
 #  ifndef M_PI_2
-#    define M_PI_2     1.57079632679489661923132169163975144
+#    define M_PI_2     FLOAT_C(1.57079632679489661923132169163975144)
 #  endif
 #  ifndef M_PI_4
-#    define M_PI_4     0.785398163397448309615660845819875721
+#    define M_PI_4     FLOAT_C(0.785398163397448309615660845819875721)
 #  endif
 #  ifndef M_1_PI
-#    define M_1_PI     0.318309886183790671537767526745028724
+#    define M_1_PI     FLOAT_C(0.318309886183790671537767526745028724)
 #  endif
 #  ifndef M_2_PI
-#    define M_2_PI     0.636619772367581343075535053490057448
+#    define M_2_PI     FLOAT_C(0.636619772367581343075535053490057448)
 #  endif
 #  ifndef M_2_SQRTPI
-#    define M_2_SQRTPI 1.12837916709551257389615890312154517
+#    define M_2_SQRTPI FLOAT_C(1.12837916709551257389615890312154517)
 #  endif
 #  ifndef M_SQRT2
-#    define M_SQRT2    1.41421356237309504880168872420969808
+#    define M_SQRT2    FLOAT_C(1.41421356237309504880168872420969808)
 #  endif
 #  ifndef M_SQRT1_2
-#    define M_SQRT1_2  0.707106781186547524400844362104849039
+#    define M_SQRT1_2  FLOAT_C(0.707106781186547524400844362104849039)
 #  endif
 
 #endif
 #  define c99_log1p    log1pq
 #  define c99_log2     log2q
 /* no logbq */
-/* no llrintq */
-/* no llroundq */
-#  define c99_lrint    lrintq
-#  define c99_lround   lroundq
+#  if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG
+#    define c99_lrint  llrintq
+#    define c99_lround llroundq
+#  else
+#    define c99_lrint  lrintq
+#    define c99_lround lroundq
+#  endif
 #  define c99_nan      nanq
 #  define c99_nearbyint        nearbyintq
 #  define c99_nextafter        nextafterq
 #  define c99_log1p    log1pl
 #  define c99_log2     log2l
 #  define c99_logb     logbl
-#  if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG
-#   define c99_lrint   llrintl
-#  else
+#  if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG && defined(HAS_LLRINTL)
+#    define c99_lrint  llrintl
+#  elif defined(HAS_LRINTL)
 #    define c99_lrint  lrintl
 #  endif
-#  if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG
+#  if defined(USE_64_BIT_INT) && QUADKIND == QUAD_IS_LONG_LONG && defined(HAS_LLROUNDL)
 #    define c99_lround llroundl
-#  else
+#  elif defined(HAS_LROUNDL)
 #    define c99_lround lroundl
 #  endif
 #  define c99_nan      nanl
 #  define c99_trunc    trunc
 #endif
 
+/* AIX xlc (__IBMC__) really doesn't have the following long double
+ * math interfaces (no __acoshl128 aka acoshl, etc.), see
+ * hints/aix.sh.  These are in the -lc128 but fail to be found
+ * during dynamic linking/loading.
+ *
+ * XXX1 Better Configure scans
+ * XXX2 Is this xlc version dependent? */
+#if defined(USE_LONG_DOUBLE) && defined(__IBMC__)
+#  undef c99_acosh
+#  undef c99_asinh
+#  undef c99_atanh
+#  undef c99_cbrt
+#  undef c99_copysign
+#  undef c99_exp2
+#  undef c99_expm1
+#  undef c99_fdim
+#  undef c99_fma
+#  undef c99_fmax
+#  undef c99_fmin
+#  undef c99_hypot
+#  undef c99_ilogb
+#  undef c99_lrint
+#  undef c99_lround
+#  undef c99_log1p
+#  undef c99_log2
+#  undef c99_logb
+#  undef c99_nan
+#  undef c99_nearbyint
+#  undef c99_nextafter
+#  undef c99_nexttoward
+#  undef c99_remainder
+#  undef c99_remquo
+#  undef c99_rint
+#  undef c99_round
+#  undef c99_scalbn
+#  undef c99_tgamma
+#  undef c99_trunc
+#endif
+
 #ifndef isunordered
 #  ifdef Perl_isnan
 #    define isunordered(x, y) (Perl_isnan(x) || Perl_isnan(y))
@@ -740,16 +801,18 @@ static IV my_ilogb(NV x)
 /* Note that the tgamma() and lgamma() implementations
  * here depend on each other. */
 
-#ifndef HAS_TGAMMA
+#if !defined(HAS_TGAMMA) || !defined(c99_tgamma)
 static NV my_tgamma(NV x);
 #  define c99_tgamma my_tgamma
+#  define USE_MY_TGAMMA
 #endif
-#ifndef HAS_LGAMMA
+#if !defined(HAS_LGAMMA) || !defined(c99_lgamma)
 static NV my_lgamma(NV x);
 #  define c99_lgamma my_lgamma
+#  define USE_MY_LGAMMA
 #endif
 
-#ifndef HAS_TGAMMA
+#ifdef USE_MY_TGAMMA
 static NV my_tgamma(NV x)
 {
   const NV gamma = 0.577215664901532860606512090; /* Euler's gamma constant. */
@@ -843,7 +906,7 @@ static NV my_tgamma(NV x)
 }
 #endif
 
-#ifndef HAS_LGAMMA
+#ifdef USE_MY_LGAMMA
 static NV my_lgamma(NV x)
 {
   if (Perl_isnan(x))
@@ -1055,6 +1118,167 @@ static NV my_trunc(NV x)
 #  define c99_trunc my_trunc
 #endif
 
+#undef NV_PAYLOAD_DEBUG
+
+/* NOTE: the NaN payload API implementation is hand-rolled, since the
+ * APIs are only proposed ones as of June 2015, so very few, if any,
+ * platforms have implementations yet, so HAS_SETPAYLOAD and such are
+ * unlikely to be helpful.
+ *
+ * XXX - if the core numification wants to actually generate
+ * the nan payload in "nan(123)", and maybe "nans(456)", for
+ * signaling payload", this needs to be moved to e.g. numeric.c
+ * (look for grok_infnan)
+ *
+ * Conversely, if the core stringification wants the nan payload
+ * and/or the nan quiet/signaling distinction, S_getpayload()
+ * from this file needs to be moved, to e.g. sv.c (look for S_infnan_2pv),
+ * and the (trivial) functionality of issignaling() copied
+ * (for generating "NaNS", or maybe even "NaNQ") -- or maybe there
+ * are too many formatting parameters for simple stringification?
+ */
+
+/* While it might make sense for the payload to be UV or IV,
+ * to avoid conversion loss, the proposed ISO interfaces use
+ * a floating point input, which is then truncated to integer,
+ * and only the integer part being used.  This is workable,
+ * except for: (1) the conversion loss (2) suboptimal for
+ * 32-bit integer platforms.  A workaround API for (2) and
+ * in general for bit-honesty would be an array of integers
+ * as the payload... but the proposed C API does nothing of
+ * the kind. */
+#if NVSIZE == UVSIZE
+#  define NV_PAYLOAD_TYPE UV
+#else
+#  define NV_PAYLOAD_TYPE NV
+#endif
+
+#ifdef LONGDOUBLE_DOUBLEDOUBLE
+#  define NV_PAYLOAD_SIZEOF_ASSERT(a) assert(sizeof(a) == NVSIZE / 2)
+#else
+#  define NV_PAYLOAD_SIZEOF_ASSERT(a) assert(sizeof(a) == NVSIZE)
+#endif
+
+static void S_setpayload(NV* nvp, NV_PAYLOAD_TYPE payload, bool signaling)
+{
+  dTHX;
+  static const U8 m[] = { NV_NAN_PAYLOAD_MASK };
+  static const U8 p[] = { NV_NAN_PAYLOAD_PERM };
+  UV a[(NVSIZE + UVSIZE - 1) / UVSIZE] = { 0 };
+  int i;
+  NV_PAYLOAD_SIZEOF_ASSERT(m);
+  NV_PAYLOAD_SIZEOF_ASSERT(p);
+  *nvp = NV_NAN;
+  /* Divide the input into the array in "base unsigned integer" in
+   * little-endian order.  Note that the integer might be smaller than
+   * an NV (if UV is U32, for example). */
+#if NVSIZE == UVSIZE
+  a[0] = payload;  /* The trivial case. */
+#else
+  {
+    NV t1 = c99_trunc(payload); /* towards zero (drop fractional) */
+#ifdef NV_PAYLOAD_DEBUG
+    Perl_warn(aTHX_ "t1 = %"NVgf" (payload %"NVgf")\n", t1, payload);
+#endif
+    if (t1 <= UV_MAX) {
+      a[0] = (UV)t1;  /* Fast path, also avoids rounding errors (right?) */
+    } else {
+      /* UVSIZE < NVSIZE or payload > UV_MAX.
+       *
+       * This may happen for example if:
+       * (1) UVSIZE == 32 and common 64-bit double NV
+       *     (32-bit system not using -Duse64bitint)
+       * (2) UVSIZE == 64 and the x86-style 80-bit long double NV
+       *     (note that here the room for payload is actually the 64 bits)
+       * (3) UVSIZE == 64 and the 128-bit IEEE 764 quadruple NV
+       *     (112 bits in mantissa, 111 bits room for payload)
+       *
+       * NOTE: this is very sensitive to correctly functioning
+       * fmod()/fmodl(), and correct casting of big-unsigned-integer to NV.
+       * If these don't work right, especially the low order bits
+       * are in danger.  For example Solaris and AIX seem to have issues
+       * here, especially if using 32-bit UVs. */
+      NV t2;
+      for (i = 0, t2 = t1; i < (int)C_ARRAY_LENGTH(a); i++) {
+        a[i] = (UV)Perl_fmod(t2, (NV)UV_MAX);
+        t2 = Perl_floor(t2 / (NV)UV_MAX);
+      }
+    }
+  }
+#endif
+#ifdef NV_PAYLOAD_DEBUG
+  for (i = 0; i < (int)C_ARRAY_LENGTH(a); i++) {
+    Perl_warn(aTHX_ "a[%d] = 0x%"UVxf"\n", i, a[i]);
+  }
+#endif
+  for (i = 0; i < (int)sizeof(p); i++) {
+    if (m[i] && p[i] < sizeof(p)) {
+      U8 s = (p[i] % UVSIZE) << 3;
+      UV u = a[p[i] / UVSIZE] & ((UV)0xFF << s);
+      U8 b = (U8)((u >> s) & m[i]);
+      ((U8 *)(nvp))[i] &= ~m[i]; /* For NaNs with non-zero payload bits. */
+      ((U8 *)(nvp))[i] |= b;
+#ifdef NV_PAYLOAD_DEBUG
+      Perl_warn(aTHX_ "set p[%2d] = %02x (i = %d, m = %02x, s = %2d, b = %02x, u = %08"UVxf")\n", i, ((U8 *)(nvp))[i], i, m[i], s, b, u);
+#endif
+      a[p[i] / UVSIZE] &= ~u;
+    }
+  }
+  if (signaling) {
+    NV_NAN_SET_SIGNALING(nvp);
+  }
+#ifdef USE_LONG_DOUBLE
+# if LONG_DOUBLEKIND == 3 || LONG_DOUBLEKIND == 4
+  memset((char *)nvp + 10, '\0', LONG_DOUBLESIZE - 10); /* x86 long double */
+# endif
+#endif
+  for (i = 0; i < (int)C_ARRAY_LENGTH(a); i++) {
+    if (a[i]) {
+      Perl_warn(aTHX_ "payload lost bits (%"UVxf")", a[i]);
+      break;
+    }
+  }
+#ifdef NV_PAYLOAD_DEBUG
+  for (i = 0; i < NVSIZE; i++) {
+    PerlIO_printf(Perl_debug_log, "%02x ", ((U8 *)(nvp))[i]);
+  }
+  PerlIO_printf(Perl_debug_log, "\n");
+#endif
+}
+
+static NV_PAYLOAD_TYPE S_getpayload(NV nv)
+{
+  dTHX;
+  static const U8 m[] = { NV_NAN_PAYLOAD_MASK };
+  static const U8 p[] = { NV_NAN_PAYLOAD_PERM };
+  UV a[(NVSIZE + UVSIZE - 1) / UVSIZE] = { 0 };
+  int i;
+  NV payload;
+  NV_PAYLOAD_SIZEOF_ASSERT(m);
+  NV_PAYLOAD_SIZEOF_ASSERT(p);
+  payload = 0;
+  for (i = 0; i < (int)sizeof(p); i++) {
+    if (m[i] && p[i] < NVSIZE) {
+      U8 s = (p[i] % UVSIZE) << 3;
+      a[p[i] / UVSIZE] |= (UV)(((U8 *)(&nv))[i] & m[i]) << s;
+    }
+  }
+  for (i = (int)C_ARRAY_LENGTH(a) - 1; i >= 0; i--) {
+#ifdef NV_PAYLOAD_DEBUG
+    Perl_warn(aTHX_ "a[%d] = %"UVxf"\n", i, a[i]);
+#endif
+    payload *= UV_MAX;
+    payload += a[i];
+  }
+#ifdef NV_PAYLOAD_DEBUG
+  for (i = 0; i < NVSIZE; i++) {
+    PerlIO_printf(Perl_debug_log, "%02x ", ((U8 *)(&nv))[i]);
+  }
+  PerlIO_printf(Perl_debug_log, "\n");
+#endif
+  return payload;
+}
+
 /* 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
@@ -1600,7 +1824,6 @@ MODULE = POSIX            PACKAGE = POSIX
 BOOT:
 {
     CV *cv;
-    const char *file = __FILE__;
 
 
     /* silence compiler warning about not_here() defined but not used */
@@ -1609,37 +1832,37 @@ BOOT:
     /* Ensure we get the function, not a macro implementation. Like the C89
        standard says we can...  */
 #undef isalnum
-    cv = newXS("POSIX::isalnum", is_common, file);
+    cv = newXS_deffile("POSIX::isalnum", is_common);
     XSANY.any_dptr = (any_dptr_t) &isalnum;
 #undef isalpha
-    cv = newXS("POSIX::isalpha", is_common, file);
+    cv = newXS_deffile("POSIX::isalpha", is_common);
     XSANY.any_dptr = (any_dptr_t) &isalpha;
 #undef iscntrl
-    cv = newXS("POSIX::iscntrl", is_common, file);
+    cv = newXS_deffile("POSIX::iscntrl", is_common);
     XSANY.any_dptr = (any_dptr_t) &iscntrl;
 #undef isdigit
-    cv = newXS("POSIX::isdigit", is_common, file);
+    cv = newXS_deffile("POSIX::isdigit", is_common);
     XSANY.any_dptr = (any_dptr_t) &isdigit;
 #undef isgraph
-    cv = newXS("POSIX::isgraph", is_common, file);
+    cv = newXS_deffile("POSIX::isgraph", is_common);
     XSANY.any_dptr = (any_dptr_t) &isgraph;
 #undef islower
-    cv = newXS("POSIX::islower", is_common, file);
+    cv = newXS_deffile("POSIX::islower", is_common);
     XSANY.any_dptr = (any_dptr_t) &islower;
 #undef isprint
-    cv = newXS("POSIX::isprint", is_common, file);
+    cv = newXS_deffile("POSIX::isprint", is_common);
     XSANY.any_dptr = (any_dptr_t) &isprint;
 #undef ispunct
-    cv = newXS("POSIX::ispunct", is_common, file);
+    cv = newXS_deffile("POSIX::ispunct", is_common);
     XSANY.any_dptr = (any_dptr_t) &ispunct;
 #undef isspace
-    cv = newXS("POSIX::isspace", is_common, file);
+    cv = newXS_deffile("POSIX::isspace", is_common);
     XSANY.any_dptr = (any_dptr_t) &isspace;
 #undef isupper
-    cv = newXS("POSIX::isupper", is_common, file);
+    cv = newXS_deffile("POSIX::isupper", is_common);
     XSANY.any_dptr = (any_dptr_t) &isupper;
 #undef isxdigit
-    cv = newXS("POSIX::isxdigit", is_common, file);
+    cv = newXS_deffile("POSIX::isxdigit", is_common);
     XSANY.any_dptr = (any_dptr_t) &isxdigit;
 }
 
@@ -1914,7 +2137,7 @@ WEXITSTATUS(status)
 #endif
            break;
        default:
-           Perl_croak(aTHX_ "Illegal alias %d for POSIX::W*", (int)ix);
+           croak("Illegal alias %d for POSIX::W*", (int)ix);
        }
     OUTPUT:
        RETVAL
@@ -1942,7 +2165,8 @@ localeconv()
 
         /* localeconv() deals with both LC_NUMERIC and LC_MONETARY, but
          * LC_MONETARY is already in the correct locale */
-        STORE_NUMERIC_STANDARD_FORCE_LOCAL();
+        DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
+        STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
 
        RETVAL = newHV();
        sv_2mortal((SV*)RETVAL);
@@ -1951,7 +2175,7 @@ localeconv()
            const struct lconv_offset *integers = lconv_integers;
            const char *ptr = (const char *) lcbuf;
 
-           do {
+           while (strings->name) {
                 /* This string may be controlled by either LC_NUMERIC, or
                  * LC_MONETARY */
                 bool is_utf8_locale
@@ -1982,18 +2206,20 @@ localeconv()
                                         && ! is_invariant_string((U8 *) value, 0)
                                         && is_utf8_string((U8 *) value, 0)),
                         0);
-                  }
-           } while ((++strings)->name);
+                }
+                strings++;
+           }
 
-           do {
+           while (integers->name) {
                const char value = *((const char *)(ptr + integers->offset));
 
                if (value != CHAR_MAX)
                    (void) hv_store(RETVAL, integers->name,
                                    strlen(integers->name), newSViv(value), 0);
-           } while ((++integers)->name);
+                integers++;
+            }
        }
-        RESTORE_NUMERIC_STANDARD();
+        RESTORE_LC_NUMERIC_STANDARD();
 #endif  /* HAS_LOCALECONV */
     OUTPUT:
        RETVAL
@@ -2017,7 +2243,7 @@ setlocale(category, locale = 0)
             }
 #   ifdef LC_ALL
             else if (category == LC_ALL) {
-                SET_NUMERIC_LOCAL();
+                SET_NUMERIC_UNDERLYING();
             }
 #   endif
         }
@@ -2039,8 +2265,8 @@ setlocale(category, locale = 0)
         /* Save retval since subsequent setlocale() calls may overwrite it. */
         retval = savepv(retval);
 
-        /* For locale == 0, we may have switched to NUMERIC_LOCAL.  Switch back
-         * */
+        /* For locale == 0, we may have switched to NUMERIC_UNDERLYING.  Switch
+         * back */
         if (locale == 0) {
             SET_NUMERIC_STANDARD();
             XSRETURN_PV(retval);
@@ -2260,6 +2486,8 @@ acos(x)
        case 20:
 #ifdef c99_logb
            RETVAL = c99_logb(x);
+#elif defined(c99_log2) && FLT_RADIX == 2
+           RETVAL = Perl_floor(c99_log2(PERL_ABS(x)));
 #else
            not_here("logb");
 #endif
@@ -2441,6 +2669,41 @@ fpclassify(x)
        RETVAL
 
 NV
+getpayload(nv)
+       NV nv
+    CODE:
+       RETVAL = S_getpayload(nv);
+    OUTPUT:
+       RETVAL
+
+void
+setpayload(nv, payload)
+       NV nv
+       NV payload
+    CODE:
+       S_setpayload(&nv, payload, FALSE);
+    OUTPUT:
+       nv
+
+void
+setpayloadsig(nv, payload)
+       NV nv
+       NV payload
+    CODE:
+       nv = NV_NAN;
+       S_setpayload(&nv, payload, TRUE);
+    OUTPUT:
+       nv
+
+int
+issignaling(nv)
+       NV nv
+    CODE:
+       RETVAL = Perl_isnan(nv) && NV_NAN_IS_SIGNALING(&nv);
+    OUTPUT:
+       RETVAL
+
+NV
 copysign(x,y)
        NV              x
        NV              y
@@ -2640,16 +2903,27 @@ fma(x,y,z)
        RETVAL
 
 NV
-nan(s = 0)
-       char*   s;
+nan(payload = 0)
+       NV payload
     CODE:
-       PERL_UNUSED_VAR(s);
-#ifdef c99_nan
-       RETVAL = c99_nan(s ? s : "");
-#elif defined(NV_NAN)
-       /* XXX if s != NULL, warn about unused argument,
-         * or implement the nan payload setting. */
-       RETVAL = NV_NAN;
+#ifdef NV_NAN
+        /* If no payload given, just return the default NaN.
+         * This makes a difference in platforms where the default
+         * NaN is not all zeros. */
+       if (items == 0) {
+          RETVAL = NV_NAN;
+       } else {
+          S_setpayload(&RETVAL, payload, FALSE);
+        }
+#elif defined(c99_nan)
+       {
+         STRLEN elen = my_snprintf(PL_efloatbuf, PL_efloatsize, "%g", nv);
+          if ((IV)elen == -1) {
+           RETVAL = NV_NAN;
+          } else {
+            RETVAL = c99_nan(PL_efloatbuf);
+          }
+        }
 #else
        not_here("nan");
 #endif
@@ -2936,9 +3210,14 @@ lseek(fd, offset, whence)
        Off_t           offset
        int             whence
     CODE:
-       Off_t pos = PerlLIO_lseek(fd, offset, whence);
-       RETVAL = sizeof(Off_t) > sizeof(IV)
-                ? newSVnv((NV)pos) : newSViv((IV)pos);
+       if (fd >= 0) {
+            Off_t pos = PerlLIO_lseek(fd, offset, whence);
+            RETVAL = sizeof(Off_t) > sizeof(IV)
+              ? newSVnv((NV)pos) : newSViv((IV)pos);
+        } else {
+            SETERRNO(EBADF,RMS_IFI);
+            RETVAL = newSViv(-1);
+        }
     OUTPUT:
        RETVAL
 
@@ -3089,17 +3368,18 @@ strtod(str)
        double num;
        char *unparsed;
     PPCODE:
-        STORE_NUMERIC_STANDARD_FORCE_LOCAL();
+        DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
+        STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
        num = strtod(str, &unparsed);
        PUSHs(sv_2mortal(newSVnv(num)));
-       if (GIMME == G_ARRAY) {
+       if (GIMME_V == G_ARRAY) {
            EXTEND(SP, 1);
            if (unparsed)
                PUSHs(sv_2mortal(newSViv(strlen(unparsed))));
            else
                PUSHs(&PL_sv_undef);
        }
-        RESTORE_NUMERIC_STANDARD();
+        RESTORE_LC_NUMERIC_STANDARD();
 
 #ifdef HAS_STRTOLD
 
@@ -3110,17 +3390,18 @@ strtold(str)
        long double num;
        char *unparsed;
     PPCODE:
-        STORE_NUMERIC_STANDARD_FORCE_LOCAL();
+        DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
+        STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
        num = strtold(str, &unparsed);
        PUSHs(sv_2mortal(newSVnv(num)));
-       if (GIMME == G_ARRAY) {
+       if (GIMME_V == G_ARRAY) {
            EXTEND(SP, 1);
            if (unparsed)
                PUSHs(sv_2mortal(newSViv(strlen(unparsed))));
            else
                PUSHs(&PL_sv_undef);
        }
-        RESTORE_NUMERIC_STANDARD();
+        RESTORE_LC_NUMERIC_STANDARD();
 
 #endif
 
@@ -3139,7 +3420,7 @@ strtol(str, base = 0)
        else
 #endif
            PUSHs(sv_2mortal(newSViv((IV)num)));
-       if (GIMME == G_ARRAY) {
+       if (GIMME_V == G_ARRAY) {
            EXTEND(SP, 1);
            if (unparsed)
                PUSHs(sv_2mortal(newSViv(strlen(unparsed))));
@@ -3164,7 +3445,7 @@ strtoul(str, base = 0)
        else
 #endif
            PUSHs(sv_2mortal(newSViv((IV)num)));
-       if (GIMME == G_ARRAY) {
+       if (GIMME_V == G_ARRAY) {
            EXTEND(SP, 1);
            if (unparsed)
                PUSHs(sv_2mortal(newSViv(strlen(unparsed))));