Self-consistent numeric conversion again
Ilya Zakharevich [Fri, 30 Apr 1999 22:26:09 +0000 (18:26 -0400)]
Message-Id: <199905010226.WAA19127@monk.mps.ohio-state.edu>

p4raw-id: //depot/perl@3378

MANIFEST
doio.c
dump.c
perl.h
pp.c
pp_hot.c
sv.c
sv.h
t/op/numconvert.t [new file with mode: 0755]
toke.c
util.c

index d1a0d98..6eefb0d 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -1204,6 +1204,7 @@ t/op/method.t             See if method calls work
 t/op/misc.t            See if miscellaneous bugs have been fixed
 t/op/mkdir.t           See if mkdir works
 t/op/my.t              See if lexical scoping works
+t/op/numconvert.t      See if accessing fields does not change numeric values
 t/op/nothread.t                local @_ test which does not work threaded 
 t/op/oct.t             See if oct and hex work
 t/op/ord.t             See if ord works
diff --git a/doio.c b/doio.c
index 064b0ca..52acbde 100644 (file)
--- a/doio.c
+++ b/doio.c
@@ -913,7 +913,10 @@ do_print(register SV *sv, PerlIO *fp)
        if (SvIOK(sv)) {
            if (SvGMAGICAL(sv))
                mg_get(sv);
-           PerlIO_printf(fp, "%ld", (long)SvIVX(sv));
+           if (SvIsUV(sv))             /* XXXX 64-bit? */
+               PerlIO_printf(fp, "%lu", (unsigned long)SvUVX(sv));
+           else
+               PerlIO_printf(fp, "%ld", (long)SvIVX(sv));
            return !PerlIO_error(fp);
        }
        /* FALL THROUGH */
diff --git a/dump.c b/dump.c
index 8f90e60..811fe78 100644 (file)
--- a/dump.c
+++ b/dump.c
@@ -279,8 +279,12 @@ sv_peek(SV *sv)
        SET_NUMERIC_STANDARD();
        sv_catpvf(t, "(%g)",SvNVX(sv));
     }
-    else if (SvIOKp(sv))
-       sv_catpvf(t, "(%ld)",(long)SvIVX(sv));
+    else if (SvIOKp(sv)) {             /* XXXX: IV, UV? */
+       if (SvIsUV(sv))
+           sv_catpvf(t, "(%lu)",(unsigned long)SvUVX(sv));
+       else
+            sv_catpvf(t, "(%ld)",(long)SvIVX(sv));
+    }
     else
        sv_catpv(t, "()");
     
@@ -781,6 +785,7 @@ do_sv_dump(I32 level, PerlIO *file, SV *sv, I32 nest, I32 maxnest, bool dumpops,
        if (CvCLONE(sv))        sv_catpv(d, "CLONE,");
        if (CvCLONED(sv))       sv_catpv(d, "CLONED,");
        if (CvNODEBUG(sv))      sv_catpv(d, "NODEBUG,");
+       if (SvCOMPILED(sv))     sv_catpv(d, "COMPILED,");
        break;
     case SVt_PVHV:
        if (HvSHAREKEYS(sv))    sv_catpv(d, "SHAREKEYS,");
@@ -803,9 +808,14 @@ do_sv_dump(I32 level, PerlIO *file, SV *sv, I32 nest, I32 maxnest, bool dumpops,
                sv_catpv(d, " ),");
            }
        }
+       /* FALL THROGH */
+    default:
+       if (SvEVALED(sv))       sv_catpv(d, "EVALED,");
+       if (SvIsUV(sv))         sv_catpv(d, "IsUV,");
+       break;
     case SVt_PVBM:
        if (SvTAIL(sv))         sv_catpv(d, "TAIL,");
-       if (SvCOMPILED(sv))     sv_catpv(d, "COMPILED,");
+       if (SvVALID(sv))        sv_catpv(d, "VALID,");
        break;
     }
 
@@ -869,7 +879,10 @@ do_sv_dump(I32 level, PerlIO *file, SV *sv, I32 nest, I32 maxnest, bool dumpops,
        return;
     }
     if (type >= SVt_PVIV || type == SVt_IV) {
-       dump_indent(level, file, "  IV = %ld", (long)SvIVX(sv));
+       if (SvIsUV(sv))
+           dump_indent(level, file, "  UV = %lu", (unsigned long)SvUVX(sv));
+       else
+           dump_indent(level, file, "  IV = %ld", (long)SvIVX(sv));
        if (SvOOK(sv))
            PerlIO_printf(file, "  (OFFSET)");
        PerlIO_putc(file, '\n');
diff --git a/perl.h b/perl.h
index 1e27d2c..e77e585 100644 (file)
--- a/perl.h
+++ b/perl.h
@@ -1652,6 +1652,11 @@ typedef I32 CHECKPOINT;
 #define U_V(what) (cast_uv((double)(what)))
 #endif
 
+/* Used with UV/IV arguments: */
+                                       /* XXXX: need to speed it up */
+#define CLUMP_2UV(iv)  ((iv) < 0 ? 0 : (UV)(iv))
+#define CLUMP_2IV(uv)  ((uv) > (UV)IV_MAX ? IV_MAX : (IV)(uv))
+
 struct Outrec {
     I32                o_lines;
     char       *o_str;
diff --git a/pp.c b/pp.c
index ccde9b0..34fffef 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -869,7 +869,7 @@ PP(pp_predec)
     djSP;
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(PL_no_modify);
-    if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
+    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
        SvIVX(TOPs) != IV_MIN)
     {
        --SvIVX(TOPs);
@@ -887,7 +887,7 @@ PP(pp_postinc)
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(PL_no_modify);
     sv_setsv(TARG, TOPs);
-    if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
+    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
        SvIVX(TOPs) != IV_MAX)
     {
        ++SvIVX(TOPs);
@@ -908,7 +908,7 @@ PP(pp_postdec)
     if(SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(PL_no_modify);
     sv_setsv(TARG, TOPs);
-    if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
+    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
        SvIVX(TOPs) != IV_MIN)
     {
        --SvIVX(TOPs);
index d49ec3d..deb4985 100644 (file)
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -233,7 +233,7 @@ PP(pp_preinc)
     djSP;
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(PL_no_modify);
-    if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
+    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
        SvIVX(TOPs) != IV_MAX)
     {
        ++SvIVX(TOPs);
diff --git a/sv.c b/sv.c
index 463359e..1fff726 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -1034,10 +1034,9 @@ sv_setiv_mg(register SV *sv, IV i)
 void
 sv_setuv(register SV *sv, UV u)
 {
-    if (u <= IV_MAX)
-       sv_setiv(sv, u);
-    else
-       sv_setnv(sv, (double)u);
+    sv_setiv(sv, 0);
+    SvIsUV_on(sv);
+    SvUVX(sv) = u;
 }
 
 void
@@ -1141,6 +1140,15 @@ not_a_number(SV *sv)
        warner(WARN_NUMERIC, "Argument \"%s\" isn't numeric", tmpbuf);
 }
 
+/* the number can be converted to _integer_ with atol() */
+#define IS_NUMBER_TO_INT_BY_ATOL 0x01
+#define IS_NUMBER_TO_INT_BY_ATOF 0x02 /* atol() may be != atof() */
+#define IS_NUMBER_NOT_IV        0x04 /* (IV)atof() may be != atof() */
+#define IS_NUMBER_NEG           0x08 /* not good to cache UV */
+
+/* Actually, ISO C leaves conversion of UV to IV undefined, but
+   until proven guilty, assume that things are not that bad... */
+
 IV
 sv_2iv(register SV *sv)
 {
@@ -1151,10 +1159,7 @@ sv_2iv(register SV *sv)
        if (SvIOKp(sv))
            return SvIVX(sv);
        if (SvNOKp(sv)) {
-           if (SvNVX(sv) < 0.0)
-               return I_V(SvNVX(sv));
-           else
-               return (IV) U_V(SvNVX(sv));
+           return I_V(SvNVX(sv));
        }
        if (SvPOKp(sv) && SvLEN(sv))
            return asIV(sv);
@@ -1176,10 +1181,7 @@ sv_2iv(register SV *sv)
        }
        if (SvREADONLY(sv)) {
            if (SvNOKp(sv)) {
-               if (SvNVX(sv) < 0.0)
-                   return I_V(SvNVX(sv));
-               else
-                   return (IV) U_V(SvNVX(sv));
+               return I_V(SvNVX(sv));
            }
            if (SvPOKp(sv) && SvLEN(sv))
                return asIV(sv);
@@ -1191,37 +1193,103 @@ sv_2iv(register SV *sv)
            return 0;
        }
     }
-    switch (SvTYPE(sv)) {
-    case SVt_NULL:
-       sv_upgrade(sv, SVt_IV);
-       break;
-    case SVt_PV:
-       sv_upgrade(sv, SVt_PVIV);
-       break;
-    case SVt_NV:
-       sv_upgrade(sv, SVt_PVNV);
-       break;
+    if (SvIOKp(sv)) {
+       if (SvIsUV(sv)) {
+           return (IV)(SvUVX(sv));
+       }
+       else {
+           return SvIVX(sv);
+       }
     }
     if (SvNOKp(sv)) {
+       /* We can cache the IV/UV value even if it not good enough
+        * to reconstruct NV, since the conversion to PV will prefer
+        * NV over IV/UV.                               XXXX 64-bit?
+        */
+
+       if (SvTYPE(sv) == SVt_NV)
+           sv_upgrade(sv, SVt_PVNV);
+
        (void)SvIOK_on(sv);
-       if (SvNVX(sv) < 0.0)
+       if (SvNVX(sv) < (double)IV_MAX + 0.5)
            SvIVX(sv) = I_V(SvNVX(sv));
-       else
+       else {
            SvUVX(sv) = U_V(SvNVX(sv));
+           SvIsUV_on(sv);
+         ret_iv_max:
+           DEBUG_c(PerlIO_printf(Perl_debug_log, 
+                                 "0x%lx 2iv(%lu => %ld) (as unsigned)\n",
+                                 (unsigned long)sv,
+                                 (unsigned long)SvUVX(sv), (long)(IV)SvUVX(sv)));
+           return (IV)SvUVX(sv);
+       }
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
-       (void)SvIOK_on(sv);
-       SvIVX(sv) = asIV(sv);
+       I32 numtype = looks_like_number(sv);
+
+       /* We want to avoid a possible problem when we cache an IV which
+          may be later translated to an NV, and the resulting NV is not
+          the translation of the initial data.
+         
+          This means that if we cache such an IV, we need to cache the
+          NV as well.  Moreover, we trade speed for space, and do not
+          cache the NV if not needed.
+        */
+       if (numtype & IS_NUMBER_NOT_IV) {
+           /* May be not an integer.  Need to cache NV if we cache IV
+            * - otherwise future conversion to NV will be wrong.  */
+           double d;
+
+           SET_NUMERIC_STANDARD();
+           d = atof(SvPVX(sv));
+
+           if (SvTYPE(sv) < SVt_PVNV)
+               sv_upgrade(sv, SVt_PVNV);
+           SvNVX(sv) = d;
+           (void)SvNOK_on(sv);
+           (void)SvIOK_on(sv);
+           DEBUG_c(PerlIO_printf(Perl_debug_log,
+                                 "0x%lx 2nv(%g)\n",(unsigned long)sv,
+                                 SvNVX(sv)));
+           if (SvNVX(sv) < (double)IV_MAX + 0.5)
+               SvIVX(sv) = I_V(SvNVX(sv));
+           else {
+               SvUVX(sv) = U_V(SvNVX(sv));
+               SvIsUV_on(sv);
+               goto ret_iv_max;
+           }
+       }
+       else if (numtype) {
+           /* The NV may be reconstructed from IV - safe to cache IV,
+              which may be calculated by atol(). */
+           if (SvTYPE(sv) == SVt_PV)
+               sv_upgrade(sv, SVt_PVIV);
+           (void)SvIOK_on(sv);
+           SvIVX(sv) = atol(SvPVX(sv)); /* XXXX 64-bit? */
+       }
+       else {                          /* Not a number.  Cache 0. */
+           dTHR;
+
+           if (SvTYPE(sv) < SVt_PVIV)
+               sv_upgrade(sv, SVt_PVIV);
+           SvIVX(sv) = 0;
+           (void)SvIOK_on(sv);
+           if (ckWARN(WARN_NUMERIC))
+               not_a_number(sv);
+       }
     }
     else  {
        dTHR;
        if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing && !(SvFLAGS(sv) & SVs_PADTMP))
            warner(WARN_UNINITIALIZED, PL_warn_uninit);
+       if (SvTYPE(sv) < SVt_IV)
+           /* Typically the caller expects that sv_any is not NULL now.  */
+           sv_upgrade(sv, SVt_IV);
        return 0;
     }
     DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%lx 2iv(%ld)\n",
        (unsigned long)sv,(long)SvIVX(sv)));
-    return SvIVX(sv);
+    return SvIsUV(sv) ? (IV)SvUVX(sv) : SvIVX(sv);
 }
 
 UV
@@ -1267,24 +1335,105 @@ sv_2uv(register SV *sv)
            return 0;
        }
     }
-    switch (SvTYPE(sv)) {
-    case SVt_NULL:
-       sv_upgrade(sv, SVt_IV);
-       break;
-    case SVt_PV:
-       sv_upgrade(sv, SVt_PVIV);
-       break;
-    case SVt_NV:
-       sv_upgrade(sv, SVt_PVNV);
-       break;
+    if (SvIOKp(sv)) {
+       if (SvIsUV(sv)) {
+           return SvUVX(sv);
+       }
+       else {
+           return (UV)SvIVX(sv);
+       }
     }
     if (SvNOKp(sv)) {
+       /* We can cache the IV/UV value even if it not good enough
+        * to reconstruct NV, since the conversion to PV will prefer
+        * NV over IV/UV.                               XXXX 64-bit?
+        */
+       if (SvTYPE(sv) == SVt_NV)
+           sv_upgrade(sv, SVt_PVNV);
        (void)SvIOK_on(sv);
-       SvUVX(sv) = U_V(SvNVX(sv));
+       if (SvNVX(sv) >= -0.5) {
+           SvIsUV_on(sv);
+           SvUVX(sv) = U_V(SvNVX(sv));
+       }
+       else {
+           SvIVX(sv) = I_V(SvNVX(sv));
+         ret_zero:
+           DEBUG_c(PerlIO_printf(Perl_debug_log, 
+                                 "0x%lx 2uv(%ld => %lu) (as signed)\n",
+                                 (unsigned long)sv,(long)SvIVX(sv),
+                                 (long)(UV)SvIVX(sv)));
+           return (UV)SvIVX(sv);
+       }
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
-       (void)SvIOK_on(sv);
-       SvUVX(sv) = asUV(sv);
+       I32 numtype = looks_like_number(sv);
+
+       /* We want to avoid a possible problem when we cache a UV which
+          may be later translated to an NV, and the resulting NV is not
+          the translation of the initial data.
+         
+          This means that if we cache such a UV, we need to cache the
+          NV as well.  Moreover, we trade speed for space, and do not
+          cache the NV if not needed.
+        */
+       if (numtype & IS_NUMBER_NOT_IV) {
+           /* May be not an integer.  Need to cache NV if we cache IV
+            * - otherwise future conversion to NV will be wrong.  */
+           double d;
+
+           SET_NUMERIC_STANDARD();
+           d = atof(SvPVX(sv));        /* XXXX 64-bit? */
+
+           if (SvTYPE(sv) < SVt_PVNV)
+               sv_upgrade(sv, SVt_PVNV);
+           SvNVX(sv) = d;
+           (void)SvNOK_on(sv);
+           (void)SvIOK_on(sv);
+           DEBUG_c(PerlIO_printf(Perl_debug_log,
+                                 "0x%lx 2nv(%g)\n",(unsigned long)sv,
+                                 SvNVX(sv)));
+           if (SvNVX(sv) < -0.5) {
+               SvIVX(sv) = I_V(SvNVX(sv));
+               goto ret_zero;
+           } else {
+               SvUVX(sv) = U_V(SvNVX(sv));
+               SvIsUV_on(sv);
+           }
+       }
+       else if (numtype & IS_NUMBER_NEG) {
+           /* The NV may be reconstructed from IV - safe to cache IV,
+              which may be calculated by atol(). */
+           if (SvTYPE(sv) == SVt_PV)
+               sv_upgrade(sv, SVt_PVIV);
+           (void)SvIOK_on(sv);
+           SvIVX(sv) = (IV)atol(SvPVX(sv)); /* XXXX 64-bit? */
+       }
+       else if (numtype) {             /* Non-negative */
+           /* The NV may be reconstructed from UV - safe to cache UV,
+              which may be calculated by strtoul()/atol. */
+           if (SvTYPE(sv) == SVt_PV)
+               sv_upgrade(sv, SVt_PVIV);
+           (void)SvIOK_on(sv);
+           (void)SvIsUV_on(sv);
+#ifdef HAS_STRTOUL
+           SvUVX(sv) = strtoul(SvPVX(sv), Null(char**), 10); /* XXXX 64-bit? */
+#else                  /* no atou(), but we know the number fits into IV... */
+                       /* The only problem may be if it is negative... */
+           SvUVX(sv) = (UV)atol(SvPVX(sv)); /* XXXX 64-bit? */
+#endif
+       }
+       else {                          /* Not a number.  Cache 0. */
+           dTHR;
+
+           if (SvTYPE(sv) < SVt_PVIV)
+               sv_upgrade(sv, SVt_PVIV);
+           SvUVX(sv) = 0;              /* We assume that 0s have the
+                                          same bitmap in IV and UV. */
+           (void)SvIOK_on(sv);
+           (void)SvIsUV_on(sv);
+           if (ckWARN(WARN_NUMERIC))
+               not_a_number(sv);
+       }
     }
     else  {
        if (!(SvFLAGS(sv) & SVs_PADTMP)) {
@@ -1292,11 +1441,15 @@ sv_2uv(register SV *sv)
            if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing)
                warner(WARN_UNINITIALIZED, PL_warn_uninit);
        }
+       if (SvTYPE(sv) < SVt_IV)
+           /* Typically the caller expects that sv_any is not NULL now.  */
+           sv_upgrade(sv, SVt_IV);
        return 0;
     }
+
     DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%lx 2uv(%lu)\n",
        (unsigned long)sv,SvUVX(sv)));
-    return SvUVX(sv);
+    return SvIsUV(sv) ? SvUVX(sv) : (UV)SvIVX(sv);
 }
 
 double
@@ -1315,8 +1468,12 @@ sv_2nv(register SV *sv)
            SET_NUMERIC_STANDARD();
            return atof(SvPVX(sv));
        }
-       if (SvIOKp(sv))
-           return (double)SvIVX(sv);
+       if (SvIOKp(sv)) {
+           if (SvIsUV(sv)) 
+               return (double)SvUVX(sv);
+           else
+               return (double)SvIVX(sv);
+       }       
         if (!SvROK(sv)) {
            if (!(SvFLAGS(sv) & SVs_PADTMP)) {
                dTHR;
@@ -1341,8 +1498,12 @@ sv_2nv(register SV *sv)
                SET_NUMERIC_STANDARD();
                return atof(SvPVX(sv));
            }
-           if (SvIOKp(sv))
-               return (double)SvIVX(sv);
+           if (SvIOKp(sv)) {
+               if (SvIsUV(sv)) 
+                   return (double)SvUVX(sv);
+               else
+                   return (double)SvIVX(sv);
+           }
            if (ckWARN(WARN_UNINITIALIZED))
                warner(WARN_UNINITIALIZED, PL_warn_uninit);
            return 0.0;
@@ -1362,7 +1523,7 @@ sv_2nv(register SV *sv)
     if (SvIOKp(sv) &&
            (!SvPOKp(sv) || !strchr(SvPVX(sv),'.') || !looks_like_number(sv)))
     {
-       SvNVX(sv) = (double)SvIVX(sv);
+       SvNVX(sv) = SvIsUV(sv) ? (double)SvUVX(sv) : (double)SvIVX(sv);
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
        dTHR;
@@ -1375,6 +1536,9 @@ sv_2nv(register SV *sv)
        dTHR;
        if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing && !(SvFLAGS(sv) & SVs_PADTMP))
            warner(WARN_UNINITIALIZED, PL_warn_uninit);
+       if (SvTYPE(sv) < SVt_NV)
+           /* Typically the caller expects that sv_any is not NULL now.  */
+           sv_upgrade(sv, SVt_NV);
        return 0.0;
     }
     SvNOK_on(sv);
@@ -1390,8 +1554,8 @@ asIV(SV *sv)
     I32 numtype = looks_like_number(sv);
     double d;
 
-    if (numtype == 1)
-       return atol(SvPVX(sv));
+    if (numtype & IS_NUMBER_TO_INT_BY_ATOL)
+       return atol(SvPVX(sv));         /* XXXX 64-bit? */
     if (!numtype) {
        dTHR;
        if (ckWARN(WARN_NUMERIC))
@@ -1399,10 +1563,7 @@ asIV(SV *sv)
     }
     SET_NUMERIC_STANDARD();
     d = atof(SvPVX(sv));
-    if (d < 0.0)
-       return I_V(d);
-    else
-       return (IV) U_V(d);
+    return I_V(d);
 }
 
 STATIC UV
@@ -1411,7 +1572,7 @@ asUV(SV *sv)
     I32 numtype = looks_like_number(sv);
 
 #ifdef HAS_STRTOUL
-    if (numtype == 1)
+    if (numtype & IS_NUMBER_TO_INT_BY_ATOL)
        return strtoul(SvPVX(sv), Null(char**), 10);
 #endif
     if (!numtype) {
@@ -1423,13 +1584,29 @@ asUV(SV *sv)
     return U_V(atof(SvPVX(sv)));
 }
 
+/*
+ * Returns a combination of (advisory only - can get false negatives)
+ *     IS_NUMBER_TO_INT_BY_ATOL, IS_NUMBER_TO_INT_BY_ATOF, IS_NUMBER_NOT_IV,
+ *     IS_NUMBER_NEG
+ * 0 if does not look like number.
+ *
+ * In fact possible values are 0 and
+ * IS_NUMBER_TO_INT_BY_ATOL                            123
+ * IS_NUMBER_TO_INT_BY_ATOL | IS_NUMBER_NOT_IV         123.1
+ * IS_NUMBER_TO_INT_BY_ATOF | IS_NUMBER_NOT_IV         123e0
+ * with a possible addition of IS_NUMBER_NEG.
+ */
+
 I32
 looks_like_number(SV *sv)
 {
+    /* XXXX 64-bit?  It may be not IS_NUMBER_TO_INT_BY_ATOL, but
+     * using atof() may lose precision. */
     register char *s;
     register char *send;
     register char *sbegin;
-    I32 numtype;
+    register char *nbegin;
+    I32 numtype = 0;
     STRLEN len;
 
     if (SvPOK(sv)) {
@@ -1445,22 +1622,40 @@ looks_like_number(SV *sv)
     s = sbegin;
     while (isSPACE(*s))
        s++;
-    if (*s == '+' || *s == '-')
+    if (*s == '-') {
+       s++;
+       numtype = IS_NUMBER_NEG;
+    }
+    else if (*s == '+')
        s++;
 
+    nbegin = s;
+    /*
+     * we return 1 if the number can be converted to _integer_ with atol()
+     * and 2 if you need (int)atof().
+     */
+
     /* next must be digit or '.' */
     if (isDIGIT(*s)) {
         do {
            s++;
         } while (isDIGIT(*s));
+
+       if (s - nbegin >= TYPE_DIGITS(IV))      /* Cannot cache ato[ul]() */
+           numtype |= IS_NUMBER_TO_INT_BY_ATOF | IS_NUMBER_NOT_IV;
+       else
+           numtype |= IS_NUMBER_TO_INT_BY_ATOL;
+
         if (*s == '.') {
            s++;
+           numtype |= IS_NUMBER_NOT_IV;
             while (isDIGIT(*s))  /* optional digits after "." */
                 s++;
         }
     }
     else if (*s == '.') {
         s++;
+       numtype |= IS_NUMBER_TO_INT_BY_ATOL | IS_NUMBER_NOT_IV;
         /* no digits before '.' means we need digits after it */
         if (isDIGIT(*s)) {
            do {
@@ -1473,15 +1668,10 @@ looks_like_number(SV *sv)
     else
         return 0;
 
-    /*
-     * we return 1 if the number can be converted to _integer_ with atol()
-     * and 2 if you need (int)atof().
-     */
-    numtype = 1;
-
     /* we can have an optional exponent part */
     if (*s == 'e' || *s == 'E') {
-       numtype = 2;
+       numtype &= ~IS_NUMBER_NEG;
+       numtype |= IS_NUMBER_TO_INT_BY_ATOF | IS_NUMBER_NOT_IV;
        s++;
        if (*s == '+' || *s == '-')
            s++;
@@ -1498,7 +1688,7 @@ looks_like_number(SV *sv)
     if (s >= send)
        return numtype;
     if (len == 10 && memEQ(sbegin, "0 but true", 10))
-       return 1;
+       return IS_NUMBER_TO_INT_BY_ATOL;
     return 0;
 }
 
@@ -1509,13 +1699,42 @@ sv_2pv_nolen(register SV *sv)
     return sv_2pv(sv, &n_a);
 }
 
+/* We assume that buf is at least TYPE_CHARS(UV) long. */
+STATIC char *
+uiv_2buf(char *buf, IV iv, UV uv, int is_uv, char **peob)
+{
+    STRLEN len;
+    char *ptr = buf + TYPE_CHARS(UV);
+    char *ebuf = ptr;
+    int sign;
+    char *p;
+
+    if (is_uv)
+       sign = 0;
+    else if (iv >= 0) {
+       uv = iv;
+       sign = 0;
+    } else {
+       uv = -iv;
+       sign = 1;
+    }
+    do {
+       *--ptr = '0' + (uv % 10);
+    } while (uv /= 10);
+    if (sign)
+       *--ptr = '-';
+    *peob = ebuf;
+    return ptr;
+}
+
 char *
 sv_2pv(register SV *sv, STRLEN *lp)
 {
     register char *s;
     int olderrno;
     SV *tsv;
-    char tmpbuf[64];   /* Must fit sprintf/Gconvert of longest IV/NV */
+    char tbuf[64];     /* Must fit sprintf/Gconvert of longest IV/NV */
+    char *tmpbuf = tbuf;
 
     if (!sv) {
        *lp = 0;
@@ -1527,8 +1746,11 @@ sv_2pv(register SV *sv, STRLEN *lp)
            *lp = SvCUR(sv);
            return SvPVX(sv);
        }
-       if (SvIOKp(sv)) {
-           (void)sprintf(tmpbuf,"%ld",(long)SvIVX(sv));
+       if (SvIOKp(sv)) {               /* XXXX 64-bit? */
+           if (SvIsUV(sv)) 
+               (void)sprintf(tmpbuf,"%lu",(unsigned long)SvUVX(sv));
+           else
+               (void)sprintf(tmpbuf,"%ld",(long)SvIVX(sv));
            tsv = Nullsv;
            goto tokensave;
        }
@@ -1627,6 +1849,7 @@ sv_2pv(register SV *sv, STRLEN *lp)
                    sv_setpvf(tsv, "%s=%s", HvNAME(SvSTASH(sv)), s);
                else
                    sv_setpv(tsv, s);
+               /* XXXX 64-bit? */
                sv_catpvf(tsv, "(0x%lx)", (unsigned long)sv);
                goto tokensaveref;
            }
@@ -1634,14 +1857,21 @@ sv_2pv(register SV *sv, STRLEN *lp)
            return s;
        }
        if (SvREADONLY(sv)) {
-           if (SvNOKp(sv)) {
+           if (SvNOKp(sv)) {           /* See note in sv_2uv() */
+               /* XXXX 64-bit?  IV may have better precision... */
                SET_NUMERIC_STANDARD();
                Gconvert(SvNVX(sv), DBL_DIG, 0, tmpbuf);
                tsv = Nullsv;
                goto tokensave;
            }
            if (SvIOKp(sv)) {
-               (void)sprintf(tmpbuf,"%ld",(long)SvIVX(sv));
+               char *ebuf;
+
+               if (SvIsUV(sv))
+                   tmpbuf = uiv_2buf(tbuf, 0, SvUVX(sv), 1, &ebuf);
+               else
+                   tmpbuf = uiv_2buf(tbuf, SvIVX(sv), 0, 0, &ebuf);
+               *ebuf = 0;
                tsv = Nullsv;
                goto tokensave;
            }
@@ -1654,8 +1884,8 @@ sv_2pv(register SV *sv, STRLEN *lp)
            return "";
        }
     }
-    (void)SvUPGRADE(sv, SVt_PV);
-    if (SvNOKp(sv)) {
+    if (SvNOKp(sv)) {                  /* See note in sv_2uv() */
+       /* XXXX 64-bit?  IV may have better precision... */
        if (SvTYPE(sv) < SVt_PVNV)
            sv_upgrade(sv, SVt_PVNV);
        SvGROW(sv, 28);
@@ -1682,14 +1912,23 @@ sv_2pv(register SV *sv, STRLEN *lp)
 #endif
     }
     else if (SvIOKp(sv)) {
-       U32 oldIOK = SvIOK(sv);
+       U32 isIOK = SvIOK(sv);
+       char buf[TYPE_CHARS(UV)];
+       char *ebuf, *ptr;
+
        if (SvTYPE(sv) < SVt_PVIV)
            sv_upgrade(sv, SVt_PVIV);
-       olderrno = errno;       /* some Xenix systems wipe out errno here */
-       sv_setpviv(sv, SvIVX(sv));
-       errno = olderrno;
+       if (SvIsUV(sv)) {
+           ptr = uiv_2buf(buf, 0, SvUVX(sv), 1, &ebuf);
+           sv_setpvn(sv, ptr, ebuf - ptr);
+           SvIsUV_on(sv);
+       }
+       else {
+           ptr = uiv_2buf(buf, SvIVX(sv), 0, 0, &ebuf);
+           sv_setpvn(sv, ptr, ebuf - ptr);
+       }
        s = SvEND(sv);
-       if (oldIOK)
+       if (isIOK)
            SvIOK_on(sv);
        else
            SvIOKp_on(sv);
@@ -1699,6 +1938,9 @@ sv_2pv(register SV *sv, STRLEN *lp)
        if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing && !(SvFLAGS(sv) & SVs_PADTMP))
            warner(WARN_UNINITIALIZED, PL_warn_uninit);
        *lp = 0;
+       if (SvTYPE(sv) < SVt_PV)
+           /* Typically the caller expects that sv_any is not NULL now.  */
+           sv_upgrade(sv, SVt_PV);
        return "";
     }
     *lp = s - SvPVX(sv);
@@ -1834,6 +2076,8 @@ sv_setsv(SV *dstr, register SV *sstr)
            }
            (void)SvIOK_only(dstr);
            SvIVX(dstr) = SvIVX(sstr);
+           if (SvIsUV(sstr))
+               SvIsUV_on(dstr);
            SvTAINT(dstr);
            return;
        }
@@ -2076,6 +2320,8 @@ sv_setsv(SV *dstr, register SV *sstr)
        if (sflags & SVp_IOK) {
            (void)SvIOK_on(dstr);
            SvIVX(dstr) = SvIVX(sstr);
+           if (SvIsUV(sstr))
+               SvIsUV_on(dstr);
        }
        if (SvAMAGIC(sstr)) {
            SvAMAGIC_on(dstr);
@@ -2130,6 +2376,8 @@ sv_setsv(SV *dstr, register SV *sstr)
        if (sflags & SVp_IOK) {
            (void)SvIOK_on(dstr);
            SvIVX(dstr) = SvIVX(sstr);
+           if (SvIsUV(sstr))
+               SvIsUV_on(dstr);
        }
     }
     else if (sflags & SVp_NOK) {
@@ -2138,11 +2386,16 @@ sv_setsv(SV *dstr, register SV *sstr)
        if (SvIOK(sstr)) {
            (void)SvIOK_on(dstr);
            SvIVX(dstr) = SvIVX(sstr);
+           /* XXXX Do we want to set IsUV for IV(ROK)?  Be extra safe... */
+           if (SvIsUV(sstr))
+               SvIsUV_on(dstr);
        }
     }
     else if (sflags & SVp_IOK) {
        (void)SvIOK_only(dstr);
        SvIVX(dstr) = SvIVX(sstr);
+       if (SvIsUV(sstr))
+           SvIsUV_on(dstr);
     }
     else {
        if (dtype == SVt_PVGV) {
@@ -2284,7 +2537,7 @@ sv_chop(register SV *sv, register char *ptr)      /* like set but assuming ptr is in
        SvIVX(sv) = 0;
        SvFLAGS(sv) |= SVf_OOK;
     }
-    SvFLAGS(sv) &= ~(SVf_IOK|SVf_NOK|SVp_IOK|SVp_NOK);
+    SvFLAGS(sv) &= ~(SVf_IOK|SVf_NOK|SVp_IOK|SVp_NOK|SVf_IVisUV);
     delta = ptr - SvPVX(sv);
     SvLEN(sv) -= delta;
     SvCUR(sv) -= delta;
@@ -3452,11 +3705,19 @@ sv_inc(register SV *sv)
        return;
     }
     if (flags & SVp_IOK) {
-       if (SvIVX(sv) == IV_MAX)
-           sv_setnv(sv, (double)IV_MAX + 1.0);
-       else {
-           (void)SvIOK_only(sv);
-           ++SvIVX(sv);
+       if (SvIsUV(sv)) {
+           if (SvUVX(sv) == UV_MAX)
+               sv_setnv(sv, (double)UV_MAX + 1.0);
+           else
+               (void)SvIOK_only_UV(sv);
+               ++SvUVX(sv);
+       } else {
+           if (SvIVX(sv) == IV_MAX)
+               sv_setnv(sv, (double)IV_MAX + 1.0);
+           else {
+               (void)SvIOK_only(sv);
+               ++SvIVX(sv);
+           }       
        }
        return;
     }
@@ -3545,11 +3806,22 @@ sv_dec(register SV *sv)
        return;
     }
     if (flags & SVp_IOK) {
-       if (SvIVX(sv) == IV_MIN)
-           sv_setnv(sv, (double)IV_MIN - 1.0);
-       else {
-           (void)SvIOK_only(sv);
-           --SvIVX(sv);
+       if (SvIsUV(sv)) {
+           if (SvUVX(sv) == 0) {
+               (void)SvIOK_only(sv);
+               SvIVX(sv) = -1;
+           }
+           else {
+               (void)SvIOK_only_UV(sv);
+               --SvUVX(sv);
+           }       
+       } else {
+           if (SvIVX(sv) == IV_MIN)
+               sv_setnv(sv, (double)IV_MIN - 1.0);
+           else {
+               (void)SvIOK_only(sv);
+               --SvIVX(sv);
+           }       
        }
        return;
     }
@@ -3919,16 +4191,22 @@ sv_true(register SV *sv)
 IV
 sv_iv(register SV *sv)
 {
-    if (SvIOK(sv))
+    if (SvIOK(sv)) {
+       if (SvIsUV(sv))
+           return (IV)SvUVX(sv);
        return SvIVX(sv);
+    }
     return sv_2iv(sv);
 }
 
 UV
 sv_uv(register SV *sv)
 {
-    if (SvIOK(sv))
-       return SvUVX(sv);
+    if (SvIOK(sv)) {
+       if (SvIsUV(sv))
+           return SvUVX(sv);
+       return (UV)SvIVX(sv);
+    }
     return sv_2uv(sv);
 }
 
@@ -4213,41 +4491,22 @@ sv_tainted(SV *sv)
 void
 sv_setpviv(SV *sv, IV iv)
 {
-    STRLEN len;
-    char buf[TYPE_DIGITS(UV)];
-    char *ptr = buf + sizeof(buf);
-    int sign;
-    UV uv;
-    char *p;
+    char buf[TYPE_CHARS(UV)];
+    char *ebuf;
+    char *ptr = uiv_2buf(buf, iv, 0, 0, &ebuf);
 
-    sv_setpvn(sv, "", 0);
-    if (iv >= 0) {
-       uv = iv;
-       sign = 0;
-    } else {
-       uv = -iv;
-       sign = 1;
-    }
-    do {
-       *--ptr = '0' + (uv % 10);
-    } while (uv /= 10);
-    len = (buf + sizeof(buf)) - ptr;
-    /* taking advantage of SvCUR(sv) == 0 */
-    SvGROW(sv, sign + len + 1);
-    p = SvPVX(sv);
-    if (sign)
-       *p++ = '-';
-    memcpy(p, ptr, len);
-    p += len;
-    *p = '\0';
-    SvCUR(sv) = p - SvPVX(sv);
+    sv_setpvn(sv, ptr, ebuf - ptr);
 }
 
 
 void
 sv_setpviv_mg(SV *sv, IV iv)
 {
-    sv_setpviv(sv,iv);
+    char buf[TYPE_CHARS(UV)];
+    char *ebuf;
+    char *ptr = uiv_2buf(buf, iv, 0, 0, &ebuf);
+
+    sv_setpvn(sv, ptr, ebuf - ptr);
     SvSETMAGIC(sv);
 }
 
diff --git a/sv.h b/sv.h
index 92e9207..533b4c4 100644 (file)
--- a/sv.h
+++ b/sv.h
@@ -153,11 +153,15 @@ struct io {
 
 /* Some private flags. */
 
-#define SVpfm_COMPILED 0x80000000
+#define SVf_IVisUV     0x80000000      /* use XPVUV instead of XPVIV */
+
+#define SVpfm_COMPILED 0x80000000      /* FORMLINE is compiled */
 
 #define SVpbm_VALID    0x80000000
 #define SVpbm_TAIL     0x40000000
 
+#define SVrepl_EVAL    0x40000000      /* Replacement part of s///e */
+
 #define SVphv_SHAREKEYS 0x20000000     /* keys live on shared string table */
 #define SVphv_LAZYDEL  0x40000000      /* entry in xhv_eiter must be deleted */
 
@@ -320,10 +324,13 @@ struct xpvio {
 #define SvNIOK(sv)             (SvFLAGS(sv) & (SVf_IOK|SVf_NOK))
 #define SvNIOKp(sv)            (SvFLAGS(sv) & (SVp_IOK|SVp_NOK))
 #define SvNIOK_off(sv)         (SvFLAGS(sv) &= ~(SVf_IOK|SVf_NOK| \
-                                                 SVp_IOK|SVp_NOK))
+                                                 SVp_IOK|SVp_NOK|SVf_IVisUV))
 
 #define SvOK(sv)               (SvFLAGS(sv) & SVf_OK)
-#define SvOK_off(sv)           (SvFLAGS(sv) &= ~(SVf_OK|SVf_AMAGIC),   \
+#define SvOK_off(sv)           (SvFLAGS(sv) &= ~(SVf_OK|SVf_AMAGIC|    \
+                                                 SVf_IVisUV),          \
+                                                       SvOOK_off(sv))
+#define SvOK_off_exc_UV(sv)    (SvFLAGS(sv) &= ~(SVf_OK|SVf_AMAGIC),   \
                                                        SvOOK_off(sv))
 
 #define SvOKp(sv)              (SvFLAGS(sv) & (SVp_IOK|SVp_NOK|SVp_POK))
@@ -337,9 +344,20 @@ struct xpvio {
 #define SvIOK(sv)              (SvFLAGS(sv) & SVf_IOK)
 #define SvIOK_on(sv)           (SvOOK_off(sv), \
                                    SvFLAGS(sv) |= (SVf_IOK|SVp_IOK))
-#define SvIOK_off(sv)          (SvFLAGS(sv) &= ~(SVf_IOK|SVp_IOK))
+#define SvIOK_off(sv)          (SvFLAGS(sv) &= ~(SVf_IOK|SVp_IOK|SVf_IVisUV))
 #define SvIOK_only(sv)         (SvOK_off(sv), \
                                    SvFLAGS(sv) |= (SVf_IOK|SVp_IOK))
+#define SvIOK_only_UV(sv)      (SvOK_off_exc_UV(sv), \
+                                   SvFLAGS(sv) |= (SVf_IOK|SVp_IOK))
+#define SvIOK_UV(sv)           ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV))   \
+                                == (SVf_IOK|SVf_IVisUV))
+#define SvIOK_notUV(sv)                ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV))   \
+                                == SVf_IOK)
+
+#define SvIsUV(sv)             (SvFLAGS(sv) & SVf_IVisUV)
+#define SvIsUV_on(sv)          (SvFLAGS(sv) |= SVf_IVisUV)
+#define SvIsUV_off(sv)         (SvFLAGS(sv) &= ~SVf_IVisUV)
 
 #define SvNOK(sv)              (SvFLAGS(sv) & SVf_NOK)
 #define SvNOK_on(sv)           (SvFLAGS(sv) |= (SVf_NOK|SVp_NOK))
@@ -350,7 +368,7 @@ struct xpvio {
 #define SvPOK(sv)              (SvFLAGS(sv) & SVf_POK)
 #define SvPOK_on(sv)           (SvFLAGS(sv) |= (SVf_POK|SVp_POK))
 #define SvPOK_off(sv)          (SvFLAGS(sv) &= ~(SVf_POK|SVp_POK))
-#define SvPOK_only(sv)         (SvFLAGS(sv) &= ~(SVf_OK|SVf_AMAGIC),   \
+#define SvPOK_only(sv)         (SvFLAGS(sv) &= ~(SVf_OK|SVf_AMAGIC|SVf_IVisUV),        \
                                    SvFLAGS(sv) |= (SVf_POK|SVp_POK))
 
 #define SvOOK(sv)              (SvFLAGS(sv) & SVf_OOK)
@@ -423,6 +441,10 @@ struct xpvio {
 #define SvCOMPILED_on(sv)      (SvFLAGS(sv) |= SVpfm_COMPILED)
 #define SvCOMPILED_off(sv)     (SvFLAGS(sv) &= ~SVpfm_COMPILED)
 
+#define SvEVALED(sv)           (SvFLAGS(sv) & SVrepl_EVAL)
+#define SvEVALED_on(sv)                (SvFLAGS(sv) |= SVrepl_EVAL)
+#define SvEVALED_off(sv)       (SvFLAGS(sv) &= ~SVrepl_EVAL)
+
 #define SvTAIL(sv)             (SvFLAGS(sv) & SVpbm_TAIL)
 #define SvTAIL_on(sv)          (SvFLAGS(sv) |= SVpbm_TAIL)
 #define SvTAIL_off(sv)         (SvFLAGS(sv) &= ~SVpbm_TAIL)
@@ -522,12 +544,13 @@ struct xpvio {
 
 #define SvIV(sv) SvIVx(sv)
 #define SvNV(sv) SvNVx(sv)
-#define SvUV(sv) SvIVx(sv)
+#define SvUV(sv) SvUVx(sv)
 #define SvTRUE(sv) SvTRUEx(sv)
 
 #ifndef CRIPPLED_CC
 /* redefine some things to more efficient inlined versions */
 
+/* Let us hope that bitmaps for UV and IV are the same */
 #undef SvIV
 #define SvIV(sv) (SvIOK(sv) ? SvIVX(sv) : sv_2iv(sv))
 
diff --git a/t/op/numconvert.t b/t/op/numconvert.t
new file mode 100755 (executable)
index 0000000..405f721
--- /dev/null
@@ -0,0 +1,193 @@
+#!./perl
+
+#
+# test the conversion operators
+#
+# Notations:
+#
+# "N p i N vs N N":  Apply op-N, then op-p, then op-i, then reporter-N
+# Compare with application of op-N, then reporter-N
+# Right below are descriptions of different ops and reporters.
+
+# We do not use these subroutines any more, sub overhead makes a "switch"
+# solution better:
+
+# obviously, 0, 1 and 2, 3 are destructive.  (XXXX 64-bit? 4 destructive too)
+
+# *0 = sub {--$_[0]};          # -
+# *1 = sub {++$_[0]};          # +
+
+# # Converters
+# *2 = sub { $_[0] = $max_uv & $_[0]}; # U
+# *3 = sub { use integer; $_[0] += $zero}; # I
+# *4 = sub { $_[0] += $zero};  # N
+# *5 = sub { $_[0] = "$_[0]" };        # P
+
+# # Side effects
+# *6 = sub { $max_uv & $_[0]}; # u
+# *7 = sub { use integer; $_[0] + $zero};      # i
+# *8 = sub { $_[0] + $zero};   # n
+# *9 = sub { $_[0] . "" };     # p
+
+# # Reporters
+# sub a2 { sprintf "%u", $_[0] }       # U
+# sub a3 { sprintf "%d", $_[0] }       # I
+# sub a4 { sprintf "%g", $_[0] }       # N
+# sub a5 { "$_[0]" }           # P
+
+BEGIN {
+    chdir 't' if -d 't';
+    @INC = '../lib';
+}
+
+use strict 'vars';
+
+my $max_chain = $ENV{PERL_TEST_NUMCONVERTS};
+unless (defined $max_chain) {
+  my $is_debug;
+  eval <<'EOE';
+    use Config;
+    $is_debug = 1 if $Config{ccflags} =~ /-DDEBUGGING\b/;
+EOE
+  $max_chain = $is_debug ? 3 : 2;
+}
+
+# Bulk out if unsigned type is hopelessly wrong:
+my $max_uv1 = ~0;
+my $max_uv2 = sprintf "%u", $max_uv1 ** 6; # 6 is an arbitrary number here
+my $big_iv = do {use integer; $max_uv1 * 16}; # 16 is an arbitrary number here
+
+if ($max_uv1 ne $max_uv2 or $big_iv > $max_uv1) {
+  print "1..0\n# Unsigned arithmetic is not sane\n";
+  exit 0;
+}
+
+my $st_t = 4*4;                        # We try 4 initializers and 4 reporters
+
+my $num = 0;
+$num += 10**$_ - 4**$_ for 1.. $max_chain;
+$num *= $st_t;
+print "1..$num\n";             # In fact 15 times more subsubtests...
+
+my $max_uv = ~0;
+my $max_iv = int($max_uv/2);
+my $zero = 0;
+
+my $l_uv = length $max_uv;
+my $l_iv = length $max_iv;
+
+# Hope: the first digits are good
+my $larger_than_uv = substr 97 x 100, 0, $l_uv;
+my $smaller_than_iv = substr 12 x 100, 0, $l_iv;
+my $yet_smaller_than_iv = substr 97 x 100, 0, ($l_iv - 1);
+
+my @list = (1, $yet_smaller_than_iv, $smaller_than_iv, $max_iv, $max_iv + 1,
+           $max_uv, $max_uv + 1);
+unshift @list, (reverse map -$_, @list), 0; # 15 elts
+@list = map "$_", @list; # Normalize
+
+# print "@list\n";
+
+
+my @opnames = split //, "-+UINPuinp";
+
+# @list = map { 2->($_), 3->($_), 4->($_), 5->($_),  } @list; # Prepare input
+
+#print "@list\n";
+#print "'@ops'\n";
+
+my $test = 1;
+my $nok;
+for my $num_chain (1..$max_chain) {
+  my @ops = map [split //], grep /[4-9]/,
+    map { sprintf "%0${num_chain}d", $_ }  0 .. 10**$num_chain - 1;
+
+  #@ops = ([]) unless $num_chain;
+  #@ops = ([6, 4]);
+
+  # print "'@ops'\n";
+  for my $op (@ops) {
+    for my $first (2..5) {
+      for my $last (2..5) {
+       $nok = 0;
+       my @otherops = grep $_ <= 3, @$op;
+       my @curops = ($op,\@otherops);
+
+       for my $num (@list) {
+         my $inpt;
+         my @ans;
+
+         for my $short (0, 1) {
+           # undef $inpt;      # Forget all we had - some bugs were masked
+
+           $inpt = $num;       # Try to not contaminate $num...
+           $inpt = "$inpt";
+           if ($first == 2) {
+             $inpt = $max_uv & $inpt; # U 2
+           } elsif ($first == 3) {
+             use integer; $inpt += $zero; # I 3
+           } elsif ($first == 4) {
+             $inpt += $zero;   # N 4
+           } else {
+             $inpt = "$inpt";  # P 5
+           }
+
+           # Saves 20% of time - not with this logic:
+           #my $tmp = $inpt;
+           #my $tmp1 = $num;
+           #next if $num_chain > 1
+           #  and "$tmp" ne "$tmp1"; # Already the coercion gives problems...
+
+           for my $curop (@{$curops[$short]}) {
+             if ($curop < 5) {
+               if ($curop < 3) {
+                 if ($curop == 0) {
+                   --$inpt;    # - 0
+                 } elsif ($curop == 1) {
+                   ++$inpt;    # + 1
+                 } else {
+                   $inpt = $max_uv & $inpt; # U 2
+                 }
+               } elsif ($curop == 3) {
+                 use integer; $inpt += $zero;
+               } else {
+                 $inpt += $zero; # N 4
+               }
+             } elsif ($curop < 8) {
+               if ($curop == 5) {
+                 $inpt = "$inpt"; # P 5
+               } elsif ($curop == 6) {
+                 $max_uv & $inpt; # u 6
+               } else {
+                 use integer; $inpt + $zero;
+               }
+             } elsif ($curop == 8) {
+               $inpt + $zero;  # n 8
+             } else {
+               $inpt . "";     # p 9
+             }
+           }
+
+           if ($last == 2) {
+             $inpt = sprintf "%u", $inpt; # U 2
+           } elsif ($last == 3) {
+             $inpt = sprintf "%d", $inpt; # I 3
+           } elsif ($last == 4) {
+             $inpt = sprintf "%g", $inpt; # N 4
+           } else {
+             $inpt = "$inpt";  # P 5
+           }
+           push @ans, $inpt;
+         }
+         $nok++,
+           print "# '$ans[0]' ne '$ans[1]',\t$num\t=> @opnames[$first,@{$curops[0]},$last] vs @opnames[$first,@{$curops[1]},$last]\n"
+             if $ans[0] ne $ans[1];
+       }
+       print "not " if $nok;
+       print "ok $test\n";
+       #print $txt if $nok;
+       $test++;
+      }
+    }
+  }
+}
diff --git a/toke.c b/toke.c
index 709db63..e9234f6 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -823,7 +823,7 @@ sublex_done(void)
        PL_lex_casemods = 0;
        *PL_lex_casestack = '\0';
        PL_lex_starts = 0;
-       if (SvCOMPILED(PL_lex_repl)) {
+       if (SvEVALED(PL_lex_repl)) {
            PL_lex_state = LEX_INTERPNORMAL;
            PL_lex_starts++;
            /*  we don't clear PL_lex_repl here, so that we can check later
@@ -1854,7 +1854,7 @@ int yylex(PERL_YYLEX_PARAM_DECL)
            return ')';
        }
        if (PL_lex_inwhat == OP_SUBST && PL_linestr == PL_lex_repl
-           && SvCOMPILED(PL_lex_repl))
+           && SvEVALED(PL_lex_repl))
        {
            if (PL_bufptr != PL_bufend)
                croak("Bad evalled substitution pattern");
@@ -5363,7 +5363,7 @@ scan_subst(char *start)
        sv_catpvn(repl, "{ ", 2);
        sv_catsv(repl, PL_lex_repl);
        sv_catpvn(repl, " };", 2);
-       SvCOMPILED_on(repl);
+       SvEVALED_on(repl);
        SvREFCNT_dec(PL_lex_repl);
        PL_lex_repl = repl;
     }
diff --git a/util.c b/util.c
index b357aa8..8df5616 100644 (file)
--- a/util.c
+++ b/util.c
@@ -2412,8 +2412,14 @@ cast_i32(double f)
 IV
 cast_iv(double f)
 {
-    if (f >= IV_MAX)
-       return (IV) IV_MAX;
+    if (f >= IV_MAX) {
+       UV uv;
+       
+       if (f >= (double)UV_MAX)
+           return (IV) UV_MAX; 
+       uv = (UV) f;
+       return (IV)uv;
+    }
     if (f <= IV_MIN)
        return (IV) IV_MIN;
     return (IV) f;
@@ -2424,6 +2430,14 @@ cast_uv(double f)
 {
     if (f >= MY_UV_MAX)
        return (UV) MY_UV_MAX;
+    if (f < 0) {
+       IV iv;
+       
+       if (f < IV_MIN)
+           return (UV)IV_MIN;
+       iv = (IV) f;
+       return (UV) iv;
+    }
     return (UV) f;
 }