This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
double-double needs the first mantissa bits of doubles.
[perl5.git] / sv.c
diff --git a/sv.c b/sv.c
index 8fe2c7e..3724caa 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -10603,21 +10603,23 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend)
     int ix;
     int ixmin = 0, ixmax = 0;
 
-    /* XXX Inf/NaN handling in the HEXTRACT_IMPLICIT_BIT,
+    /* XXX Inf/NaN/denormal handling in the HEXTRACT_IMPLICIT_BIT,
      * and elsewhere. */
 
     /* These macros are just to reduce typos, they have multiple
      * repetitions below, but usually only one (or sometimes two)
      * of them is really being used. */
     /* HEXTRACT_OUTPUT() extracts the high nybble first. */
-#define HEXTRACT_OUTPUT() \
+#define HEXTRACT_OUTPUT_HI(ix) (*v++ = nvp[ix] >> 4)
+#define HEXTRACT_OUTPUT_LO(ix) (*v++ = nvp[ix] & 0xF)
+#define HEXTRACT_OUTPUT(ix) \
     STMT_START { \
-      *v++ = nvp[ix] >> 4; \
-      *v++ = nvp[ix] & 0xF; \
+        HEXTRACT_OUTPUT_HI(ix); \
+        HEXTRACT_OUTPUT_LO(ix); \
     } STMT_END
-#define HEXTRACT_COUNT() \
+#define HEXTRACT_COUNT(ix, c) \
     STMT_START { \
-      v += 2; \
+      v += c; \
       if (ix < ixmin) \
         ixmin = ix; \
       else if (ix > ixmax) \
@@ -10637,99 +10639,109 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend)
 #  define HEXTRACTSIZE NVSIZE
     (void)Perl_frexp(PERL_ABS(nv), exponent);
 #  if LONG_DOUBLEKIND == LONG_DOUBLE_IS_IEEE_754_128_BIT_LITTLE_ENDIAN
-    /* Used in e.g. VMS and HP-UX IA64, e.g. -0.1L:
+    /* Used in e.g. VMS and HP-UX IA-64, e.g. -0.1L:
      * 9a 99 99 99 99 99 99 99 99 99 99 99 99 99 fb 3f */
     /* The bytes 13..0 are the mantissa/fraction,
      * the 15,14 are the sign+exponent. */
     HEXTRACT_IMPLICIT_BIT();
     for (ix = 13; ix >= 0; ix--) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    *exponent -= 4;
 #  elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_IEEE_754_128_BIT_BIG_ENDIAN
-    /* Used in e.g. Solaris Sparc and HP-PA HP-UX, e.g. -0.1L:
+    /* Used in e.g. Solaris Sparc and HP-UX PA-RISC, e.g. -0.1L:
      * bf fb 99 99 99 99 99 99 99 99 99 99 99 99 99 9a */
     /* The bytes 2..15 are the mantissa/fraction,
      * the 0,1 are the sign+exponent. */
     HEXTRACT_IMPLICIT_BIT();
     for (ix = 2; ix <= 15; ix++) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    *exponent -= 4;
 #  elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_X86_80_BIT_LITTLE_ENDIAN
     /* x86 80-bit "extended precision", 64 bits of mantissa / fraction /
      * significand, 15 bits of exponent, 1 bit of sign.  NVSIZE can
      * be either 12 (ILP32, Solaris x86) or 16 (LP64, Linux and OS X),
-     * meaning that 4 or 6 bytes are empty padding. */
+     * meaning that 2 or 6 bytes are empty padding. */
     /* The bytes 7..0 are the mantissa/fraction */
     /* There explicitly is *no* implicit bit in this case. */
     for (ix = 7; ix >= 0; ix--) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    *exponent -= 4;
 #  elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_X86_80_BIT_BIG_ENDIAN
     /* The last 8 bytes are the mantissa/fraction.
      * (does this format ever happen?) */
     /* There explicitly is *no* implicit bit in this case. */
     for (ix = LONGDBLSIZE - 8; ix < LONGDBLSIZE; ix++) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    *exponent -= 4;
 #  elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_DOUBLEDOUBLE_128_BIT_LITTLE_ENDIAN
     /* Where is this used?
-     *
-     * Guessing that the format would be the reverse
-     * of big endian, i.e. for -0.1L:
-     * 9a 99 99 99 99 99 59 3c 9a 99 99 99 99 99 b9 bf */
+     * 9a 99 99 99 99 99 59 bc 9a 99 99 99 99 99 b9 3f */
     HEXTRACT_IMPLICIT_BIT();
+    if (vend)
+        HEXTRACT_OUTPUT_LO(14);
+    else
+        HEXTRACT_COUNT(14, 1);
     for (ix = 13; ix >= 8; ix--) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    if (vend)
+        HEXTRACT_OUTPUT_LO(6);
+    else
+        HEXTRACT_COUNT(6, 1);
     for (ix = 5; ix >= 0; ix--) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    (*exponent)--;
 #  elif LONG_DOUBLEKIND == LONG_DOUBLE_IS_DOUBLEDOUBLE_128_BIT_BIG_ENDIAN
-    /* Used in e.g. PPC/Power and MIPS.
-     *
-     * The mantissa bits are in two separate stretches,
-     * e.g. for -0.1L:
-     * bf b9 99 99 99 99 99 9a 3c 59 99 99 99 99 99 9a
-     * as seen in PowerPC AIX, as opposed to "true" 128-bit IEEE 754:
-     * bf fb 99 99 99 99 99 99 99 99 99 99 99 99 99 9a
-     * as seen in HP-PA HP-UX.
+    /* Used in e.g. PPC/Power (AIX) and MIPS.
      *
-     * Note that this blind copying might be considered not to be
-     * the right thing, since the first double already does
-     * rounding (0x9A as opposed to 0x99).  But then again, we
-     * probably should just copy the bits as they are?
+     * The mantissa bits are in two separate stretches, e.g. for -0.1L:
+     * 3f b9 99 99 99 99 99 9a bc 59 99 99 99 99 99 9a
      */
     HEXTRACT_IMPLICIT_BIT();
+    if (vend)
+        HEXTRACT_OUTPUT_LO(1);
+    else
+        HEXTRACT_COUNT(1, 1);
     for (ix = 2; ix < 8; ix++) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    if (vend)
+        HEXTRACT_OUTPUT_LO(9);
+    else
+        HEXTRACT_COUNT(9, 1);
     for (ix = 10; ix < 16; ix++) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
+    (*exponent)--;
 #  else
     Perl_croak(aTHX_
                "Hexadecimal float: unsupported long double format");
@@ -10780,17 +10792,17 @@ S_hextract(pTHX_ const NV nv, int* exponent, U8* vhex, U8* vend)
     /* Little endian. */
     for (ix = limit_byte; ix >= 0; ix--) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
 #  else
     /* Big endian. */
     for (ix = MANTISSASIZE - 1 - limit_byte; ix < MANTISSASIZE; ix++) {
         if (vend)
-            HEXTRACT_OUTPUT();
+            HEXTRACT_OUTPUT(ix);
         else
-            HEXTRACT_COUNT();
+            HEXTRACT_COUNT(ix, 2);
     }
 #  endif
     /* If there are not enough bits in MANTISSATYPE, we couldn't get
@@ -11828,7 +11840,7 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                  * human-readable xdigits. */
                 const char* xdig = PL_hexdigit;
                 int zerotail = 0; /* how many extra zeros to append */
-                int exponent; /* exponent of the floating point input */
+                int exponent = 0; /* exponent of the floating point input */
 
                 vend = S_hextract(aTHX_ nv, &exponent, vhex, NULL);
                 S_hextract(aTHX_ nv, &exponent, vhex, vend);
@@ -11865,9 +11877,10 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                         }
                     }
 
-                    /* Adjust the exponent so that the first output
-                     * xdigit aligns with the 4-bit nybbles. */
-                    exponent -= NV_MANT_DIG % 4 ? NV_MANT_DIG % 4 : 4;
+#if NVSIZE == DOUBLESIZE
+                    /* For long doubles S_hextract() took care of this. */
+                    exponent--;
+#endif
 
                     if (precis > 0) {
                         v = vhex + precis + 1;