File::Basename doesn't lazy load Carp right.
[p5sagit/p5-mst-13.2.git] / numeric.c
index 913ecc8..969901e 100644 (file)
--- a/numeric.c
+++ b/numeric.c
@@ -175,7 +175,7 @@ Perl_grok_bin(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
                 }
                 /* Bah. We're just overflowed.  */
                 if (ckWARN_d(WARN_OVERFLOW))
-                    Perl_warner(aTHX_ WARN_OVERFLOW,
+                    Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
                                 "Integer overflow in binary number");
                 overflowed = TRUE;
                 value_nv = (NV) value;
@@ -198,7 +198,7 @@ Perl_grok_bin(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
                 goto redo;
            }
         if (ckWARN(WARN_DIGIT))
-            Perl_warner(aTHX_ WARN_DIGIT,
+            Perl_warner(aTHX_ packWARN(WARN_DIGIT),
                         "Illegal binary digit '%c' ignored", *s);
         break;
     }
@@ -209,7 +209,7 @@ Perl_grok_bin(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
 #endif
        ) {
        if (ckWARN(WARN_PORTABLE))
-           Perl_warner(aTHX_ WARN_PORTABLE,
+           Perl_warner(aTHX_ packWARN(WARN_PORTABLE),
                        "Binary number > 0b11111111111111111111111111111111 non-portable");
     }
     *len_p = s - start;
@@ -290,7 +290,7 @@ Perl_grok_hex(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
                 }
                 /* Bah. We're just overflowed.  */
                 if (ckWARN_d(WARN_OVERFLOW))
-                    Perl_warner(aTHX_ WARN_OVERFLOW,
+                    Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
                                 "Integer overflow in hexadecimal number");
                 overflowed = TRUE;
                 value_nv = (NV) value;
@@ -313,7 +313,7 @@ Perl_grok_hex(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
                 goto redo;
            }
         if (ckWARN(WARN_DIGIT))
-            Perl_warner(aTHX_ WARN_DIGIT,
+            Perl_warner(aTHX_ packWARN(WARN_DIGIT),
                         "Illegal hexadecimal digit '%c' ignored", *s);
         break;
     }
@@ -324,7 +324,7 @@ Perl_grok_hex(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
 #endif
        ) {
        if (ckWARN(WARN_PORTABLE))
-           Perl_warner(aTHX_ WARN_PORTABLE,
+           Perl_warner(aTHX_ packWARN(WARN_PORTABLE),
                        "Hexadecimal number > 0xffffffff non-portable");
     }
     *len_p = s - start;
@@ -372,7 +372,7 @@ Perl_grok_oct(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
                 }
                 /* Bah. We're just overflowed.  */
                 if (ckWARN_d(WARN_OVERFLOW))
-                    Perl_warner(aTHX_ WARN_OVERFLOW,
+                    Perl_warner(aTHX_ packWARN(WARN_OVERFLOW),
                                 "Integer overflow in octal number");
                 overflowed = TRUE;
                 value_nv = (NV) value;
@@ -399,7 +399,7 @@ Perl_grok_oct(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
          * someone seems to want to use the digits eight and nine). */
         if (digit == 8 || digit == 9) {
             if (ckWARN(WARN_DIGIT))
-                Perl_warner(aTHX_ WARN_DIGIT,
+                Perl_warner(aTHX_ packWARN(WARN_DIGIT),
                             "Illegal octal digit '%c' ignored", *s);
         }
         break;
@@ -411,7 +411,7 @@ Perl_grok_oct(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result) {
 #endif
        ) {
        if (ckWARN(WARN_PORTABLE))
-           Perl_warner(aTHX_ WARN_PORTABLE,
+           Perl_warner(aTHX_ packWARN(WARN_PORTABLE),
                        "Octal number > 037777777777 non-portable");
     }
     *len_p = s - start;
@@ -727,10 +727,6 @@ S_mulexp10(NV value, I32 exponent)
 
     if (exponent == 0)
        return value;
-    else if (exponent < 0) {
-       negative = 1;
-       exponent = -exponent;
-    }
 
     /* On OpenVMS VAX we by default use the D_FLOAT double format,
      * and that format does not have *easy* capabilities [1] for
@@ -743,11 +739,6 @@ S_mulexp10(NV value, I32 exponent)
      *
      * [1] Trying to establish a condition handler to trap floating point
      *     exceptions is not a good idea. */
-#if defined(VMS) && !defined(__IEEE_FP) && defined(NV_MAX_10_EXP)
-    if (!negative &&
-        (log10(value) + exponent) >= (NV_MAX_10_EXP))
-        return NV_MAX;
-#endif
 
     /* In UNICOS and in certain Cray models (such as T90) there is no
      * IEEE fp, and no way at all from C to catch fp overflows gracefully.
@@ -756,18 +747,37 @@ S_mulexp10(NV value, I32 exponent)
      * disable *all* floating point interrupts, a little bit too large
      * a hammer.  Therefore we need to catch potential overflows before
      * it's too late. */
-#if defined(_UNICOS) && defined(NV_MAX_10_EXP)
-    if (!negative &&
-       (log10(value) + exponent) >= NV_MAX_10_EXP)
-        return NV_MAX;
+
+#if ((defined(VMS) && !defined(__IEEE_FP)) || defined(_UNICOS)) && defined(NV_MAX_10_EXP)
+    STMT_START {
+       NV exp_v = log10(value);
+       if (exponent >= NV_MAX_10_EXP || exponent + exp_v >= NV_MAX_10_EXP)
+           return NV_MAX;
+       if (exponent < 0) {
+           if (-(exponent + exp_v) >= NV_MAX_10_EXP)
+               return 0.0;
+           while (-exponent >= NV_MAX_10_EXP) {
+               /* combination does not overflow, but 10^(-exponent) does */
+               value /= 10;
+               ++exponent;
+           }
+       }
+    } STMT_END;
 #endif
 
+    if (exponent < 0) {
+       negative = 1;
+       exponent = -exponent;
+    }
     for (bit = 1; exponent; bit <<= 1) {
        if (exponent & bit) {
            exponent ^= bit;
            result *= power;
+           /* Floating point exceptions are supposed to be turned off,
+            *  but if we're obviously done, don't risk another iteration.  
+            */
+            if (exponent == 0) break;
        }
-       /* Floating point exceptions are supposed to be turned off. */
        power *= power;
     }
     return negative ? value / result : value * result;
@@ -783,17 +793,17 @@ Perl_my_atof(pTHX_ const char* s)
 
        /* Scan the number twice; once using locale and once without;
         * choose the larger result (in absolute value). */
-       Perl_atof2(aTHX_ s, &x);
+       Perl_atof2(s, x);
        SET_NUMERIC_STANDARD();
-       Perl_atof2(aTHX_ s, &y);
+       Perl_atof2(s, y);
        SET_NUMERIC_LOCAL();
        if ((y < 0.0 && y < x) || (y > 0.0 && y > x))
            return y;
     }
     else
-       Perl_atof2(aTHX_ s, &x);
+       Perl_atof2(s, x);
 #else
-    Perl_atof2(aTHX_ s, &x);
+    Perl_atof2(s, x);
 #endif
     return x;
 }
@@ -802,25 +812,40 @@ char*
 Perl_my_atof2(pTHX_ const char* orig, NV* value)
 {
     NV result = 0.0;
-    bool negative = 0;
     char* s = (char*)orig;
+#ifdef USE_PERL_ATOF
+    UV accumulator = 0;
+    bool negative = 0;
     char* send = s + strlen(orig) - 1;
-    bool seendigit = 0;
-    I32 expextra = 0;
+    bool seen_digit = 0;
+    I32 exp_adjust = 0;
+    I32 exp_acc = 0;   /* the current exponent adjust for the accumulator */
     I32 exponent = 0;
-    I32 i;
-/* this is arbitrary */
-#define PARTLIM 6
-/* we want the largest integers we can usefully use */
-#if defined(HAS_QUAD) && defined(USE_64_BIT_INT)
-#   define PARTSIZE ((int)TYPE_DIGITS(U64)-1)
-    U64 part[PARTLIM];
-#else
-#   define PARTSIZE ((int)TYPE_DIGITS(U32)-1)
-    U32 part[PARTLIM];
-#endif
-    I32 ipart = 0;     /* index into part[] */
-    I32 offcount;      /* number of digits in least significant part */
+    I32        seen_dp  = 0;
+    I32 digit;
+    I32 sig_digits = 0; /* noof significant digits seen so far */
+
+/* There is no point in processing more significant digits
+ * than the NV can hold. Note that NV_DIG is a lower-bound value,
+ * while we need an upper-bound value. We add 2 to account for this;
+ * since it will have been conservative on both the first and last digit.
+ * For example a 32-bit mantissa with an exponent of 4 would have
+ * exact values in the set
+ *               4
+ *               8
+ *              ..
+ *     17179869172
+ *     17179869176
+ *     17179869180
+ *
+ * where for the purposes of calculating NV_DIG we would have to discount
+ * both the first and last digit, since neither can hold all values from
+ * 0..9; but for calculating the value we must examine those two digits.
+ */
+#define MAX_SIG_DIGITS (NV_DIG+2)
+
+/* the max number we can accumulate in a UV, and still safely do 10*N+9 */
+#define MAX_ACCUMULATE ( (UV) ((UV_MAX - 9)/10))
 
     /* leading whitespace */
     while (isSPACE(*s))
@@ -835,74 +860,54 @@ Perl_my_atof2(pTHX_ const char* orig, NV* value)
            ++s;
     }
 
-    part[0] = offcount = 0;
-    if (isDIGIT(*s)) {
-       seendigit = 1;  /* get this over with */
+    /* we accumulate digits into an integer; when this becomes too
+     * large, we add the total to NV and start again */
 
-       /* skip leading zeros */
-       while (*s == '0')
-           ++s;
-    }
+    while (1) {
+       if (isDIGIT(*s)) {
+           seen_digit = 1;
+           digit = *s++ - '0';
+           exp_adjust -= seen_dp;
 
-    /* integer digits */
-    while (isDIGIT(*s)) {
-       if (++offcount > PARTSIZE) {
-           if (++ipart < PARTLIM) {
-               part[ipart] = 0;
-               offcount = 1;   /* ++0 */
-           }
-           else {
+           /* don't start counting until we see the first significant
+            * digit, eg the 5 in 0.00005... */
+           if (!sig_digits && digit == 0)
+               continue;
+
+           if (++sig_digits > MAX_SIG_DIGITS) {
                /* limits of precision reached */
-               --ipart;
-               --offcount;
-               if (*s >= '5')
-                   ++part[ipart];
+               if (digit >= 5)
+                   ++accumulator;
+               ++exp_adjust;
+               /* skip remaining digits */
                while (isDIGIT(*s)) {
-                   ++expextra;
                    ++s;
+                   exp_adjust += 1 - seen_dp;
                }
                /* warn of loss of precision? */
-               break;
            }
-       }
-       part[ipart] = part[ipart] * 10 + (*s++ - '0');
-    }
-
-    /* decimal point */
-    if (GROK_NUMERIC_RADIX((const char **)&s, send)) {
-       if (isDIGIT(*s))
-           seendigit = 1;      /* get this over with */
-
-       /* decimal digits */
-       while (isDIGIT(*s)) {
-           if (++offcount > PARTSIZE) {
-               if (++ipart < PARTLIM) {
-                   part[ipart] = 0;
-                   offcount = 1;       /* ++0 */
-               }
-               else {
-                   /* limits of precision reached */
-                   --ipart;
-                   --offcount;
-                   if (*s >= '5')
-                       ++part[ipart];
-                   while (isDIGIT(*s))
-                       ++s;
-                   /* warn of loss of precision? */
-                   break;
+           else {
+               if (accumulator > MAX_ACCUMULATE) {
+                   /* add accumulator to result and start again */
+                   result = S_mulexp10(result, exp_acc) + (NV)accumulator;
+                   accumulator = 0;
+                   exp_acc = 0;
                }
+               accumulator = accumulator * 10 + digit;
+               ++exp_acc;
            }
-           --expextra;
-           part[ipart] = part[ipart] * 10 + (*s++ - '0');
+       }
+       else if (!seen_dp && GROK_NUMERIC_RADIX((const char **)&s, send)) {
+           seen_dp = 1;
+       }
+       else {
+           break;
        }
     }
 
-    /* combine components of mantissa */
-    for (i = 0; i <= ipart; ++i)
-       result += S_mulexp10((NV)part[ipart - i],
-               i ? offcount + (i - 1) * PARTSIZE : 0);
+    result = S_mulexp10(result, exp_acc) + (NV)accumulator;
 
-    if (seendigit && (*s == 'e' || *s == 'E')) {
+    if (seen_digit && (*s == 'e' || *s == 'E')) {
        bool expnegative = 0;
 
        ++s;
@@ -920,12 +925,13 @@ Perl_my_atof2(pTHX_ const char* orig, NV* value)
     }
 
     /* now apply the exponent */
-    exponent += expextra;
+    exponent += exp_adjust;
     result = S_mulexp10(result, exponent);
 
     /* now apply the sign */
     if (negative)
        result = -result;
+#endif /* USE_PERL_ATOF */
     *value = result;
     return s;
 }