develooper Front page | perl.perl5.porters | Postings from February 2003

Re: [perl #20827] Unexpected scientific notation.

Thread Previous | Thread Next
From:
hv
Date:
February 11, 2003 19:10
Subject:
Re: [perl #20827] Unexpected scientific notation.
Message ID:
200302120312.h1C3ChS02613@crypt.compulink.co.uk
Nicholas Clark <nick@unfortu.net> wrote:
:>     $ perl -wle 'print 1597009560 ** 2'
:>     2550439534731393600
:
:exponentiation will be done as NVs
:It will be constant folded in the peephole optimiser in op.c:
:
:#ifdef PERL_PRESERVE_IVUV
:            /* Only bother to attempt to fold to IV if
:               most operators will benefit  */
:            SvIV_please(sv);
:#endif

Hmm, should this not special case integer power of an integer, and other
calculations that we can guarantee should give an integer? And leave it
to the special-casing to handle the conversion rather than this separate
blanket check? It seems quite correct for 1.4142...(30) ** 2 to give 2.000,
and I think I'd be happy for sin(0) to return 0.000.

:If anything, the peephole optimiser should be changed, and the constant
:1597009560 ** 2 left as an NV. This would make the first two the same, and
:the second two both scientific notation.

I agree they should be the same. I think the answer though is to move the
optimisation (hmm, correction) to where it belongs.

:Sanity here being that exp (37) / 2 should not be an integer.
:Actually, currently we have insanity:
:
:$ perl5.8.0-64 -le 'print exp (37)/2'
:5859571186401306
:$ perl5.8.0-64 -le 'print exp (shift) /2' 37
:5.85957118640131e+15
:
:I would consider only the latter answer sane.

I'd agree.

The simplistic patch below breaks no tests here. Is this the direction
we should be going?

Since I was looking at the code anyway, it occurred that it seems a shame
not to use direct multiplication for small numbers other than pow(2^m, n).
I'd have thought that integer powers with overlarge results other than
powers of two would be quite rare, so I think this should also be a win
for any cases we can quickly enough detect would fit in a UV; the second
patch below (over the first, but independent) is an attempt at implementing
that using a simple bitsize test, which also breaks no tests here. Comments
likewise welcomed.

Do we have a macro for 'index of the highest bit set in a UV'? ISTR there
are processors with an opcode for this ...

Also, when we know C<bit> is set, wouldn't C< power -= bit > be faster
mostwheres than C< power &= ~bit >?

And fifthly, should be returning (IV)1 when power is (IV)0, regardless
of the type (assuming non-zero) of the base?

Hugo
--- pp.c.old	Tue Feb 11 02:14:06 2003
+++ pp.c	Wed Feb 12 03:00:32 2003
@@ -879,7 +879,11 @@
 
 PP(pp_pow)
 {
-    dSP; dATARGET; tryAMAGICbin(pow,opASSIGN);
+    dSP; dATARGET;
+#ifdef PERL_PRESERVE_IVUV
+    bool is_int = 0;
+#endif
+    tryAMAGICbin(pow,opASSIGN);
 #ifdef PERL_PRESERVE_IVUV
     /* ** is implemented with pow. pow is floating point. Perl programmers
        write 2 ** 31 and expect it to be 2147483648
@@ -920,8 +924,10 @@
                         goto float_it; /* Can't do negative powers this way.  */
                     }
                 }
-                /* now we have integer ** positive integer.
-                   foo & (foo - 1) is zero only for a power of 2.  */
+		/* now we have integer ** positive integer. */
+		is_int = 1;
+
+		/* foo & (foo - 1) is zero only for a power of 2.  */
                 if (!(baseuv & (baseuv - 1))) {
                     /* We are raising power-of-2 to postive integer.
                        The logic here will work for any base (even non-integer
@@ -963,6 +969,7 @@
                     }
                     SP--;
                     SETn( result );
+		    SvIV_please(TOPs);
                     RETURN;
                 }
             }
@@ -973,6 +980,10 @@
     {
         dPOPTOPnnrl;
         SETn( Perl_pow( left, right) );
+#ifdef PERL_PRESERVE_IVUV
+	if (is_int)
+	    SvIV_please(TOPs);
+#endif
         RETURN;
     }
 }
--- pp.c	Wed Feb 12 03:00:32 2003
+++ pp.c.new	Wed Feb 12 02:58:23 2003
@@ -971,7 +971,49 @@
                     SETn( result );
 		    SvIV_please(TOPs);
                     RETURN;
-                }
+                } else {
+		    register unsigned int highbit = 8 * sizeof(UV);
+		    register unsigned int lowbit = 0;
+		    register unsigned int diff;
+		    while ((diff = (highbit - lowbit) >> 1)) {
+			if (baseuv & ~((1 << (lowbit + diff)) - 1))
+			    lowbit += diff;
+			else 
+			    highbit -= diff;
+		    }
+		    /* we now have baseuv < 2 ** highbit */
+		    if (power * highbit <= 8 * sizeof(UV)) {
+			/* result will definitely fit in UV, so use UV math
+			   on same algorithm as above */
+			UV result = 1;
+			UV base = baseuv;
+			int n = 0;
+			for (; power; base *= base, n++) {
+			    UV bit = (UV)1 << (UV)n;
+			    if (power & bit) {
+				result *= base;
+				/* Only bother to clear the bit if it is set.  */
+				power &= ~bit;
+				/* Avoid squaring base again if we're done. */
+				if (power == 0) break;
+			    }
+			}
+			SP--;
+			if (baseuok || !(power & 1))
+			    /* answer is positive */
+			    SETu( result );
+			else if (result <= (UV)IV_MAX)
+			    /* answer negative, fits in IV */
+			    SETi( -(IV)result );
+			else if (result == (UV)IV_MIN) 
+			    /* (2's complement assumption) special case IV_MIN */
+			    SETi( IV_MIN );
+			else
+			    /* answer negative, doesn't fit */
+			    SETn( -(NV)result );
+			RETURN;
+		    } 
+		}
             }
         }
     }

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About