]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Increase internal precision of ldbl-128ibm decimal printf [BZ #19853]
authorPaul E. Murphy <murphyp@linux.vnet.ibm.com>
Tue, 12 Apr 2016 18:29:53 +0000 (13:29 -0500)
committerPaul E. Murphy <murphyp@linux.vnet.ibm.com>
Tue, 12 Apr 2016 18:29:53 +0000 (13:29 -0500)
When the signs differ, the precision of the conversion sometimes
drops below 106 bits.  This strategy is identical to the
hexadecimal variant.

I've refactored tst-sprintf3 to enable testing a value with more
than 30 significant digits in order to demonstrate this failure
and its solution.

Additionally, this implicitly fixes a typo in the shift
quantities when subtracting from the high mantissa to compute
the difference.

(cherry picked from commit 37a4c70bd4c5c74ac562072e450dc02e8cb4c150)

ChangeLog
NEWS
stdio-common/tst-sprintf3.c
sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c

index b6862317143837b62f609cef9e7e8a2d24262cd2..f7664af61419b900d6487e56ed78905f1f7a65f5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2016-04-12  Paul E. Murphy  <murphyp@linux.vnet.ibm.com>
+
+       [BZ #19853]
+       * stdio-common/tst-sprintf3.c [TEST_N]: Refactor
+       TEST to take significant digits as second parameter.
+       [TEST]: Redefine in terms of TEST_N taking 30
+       significant digits.
+       (do_test): Add test case to demonstrate precision
+       failure in the ldbl-128ibm printf.
+       * sysdeps/ieee754/ldbl-128ibm/ldbl2pm.c:
+       (__mpn_extract_long_double): Carry 7 extra intermediate
+       bits of precision to aide computing difference when
+       signs differ.
+
 2016-04-09  Mike Frysinger  <vapier@gentoo.org>
 
        * sysdeps/i386/configure.ac: Change == to = when calling test.
diff --git a/NEWS b/NEWS
index 8aa1206ea8fb16ca9a9f80a2816175cf05c10c59..2a43aeca29c9f0c53eaee4d8119c295072d79afa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -25,7 +25,7 @@ Version 2.22.1
 
   17905, 18420, 18421, 18480, 18589, 18743, 18778, 18781, 18787, 18796,
   18870, 18887, 18921, 18928, 18969, 18985, 19003, 19018, 19058, 19174,
-  19178, 19590, 19682, 19791, 19822, 19879.
+  19178, 19590, 19682, 19791, 19822, 19853, 19879.
 
 * The getnetbyname implementation in nss_dns had a potentially unbounded
   alloca call (in the form of a call to strdupa), leading to a stack
index e1e5317ead60dab6ba042cc37d159261e8908a6f..cae8ed1e8f157c62b4684e7d1d082ed0a456b936 100644 (file)
@@ -38,11 +38,11 @@ do_test (void)
 # define COMPARE_LDBL(u, v) ((u).l == (v).l)
 #endif
 
-#define TEST(val) \
+#define TEST_N(val, n) \
   do                                                                      \
     {                                                                     \
       u.l = (val);                                                        \
-      snprintf (buf, sizeof buf, "%.30LgL", u.l);                         \
+      snprintf (buf, sizeof buf, "%." #n "LgL", u.l);                     \
       if (strcmp (buf, #val) != 0)                                        \
        {                                                                  \
          printf ("Error on line %d: %s != %s\n", __LINE__, buf, #val);    \
@@ -50,19 +50,25 @@ do_test (void)
        }                                                                  \
       if (sscanf (#val, "%Lg", &v.l) != 1 || !COMPARE_LDBL (u, v))        \
        {                                                                  \
-         printf ("Error sscanf on line %d: %.30Lg != %.30Lg\n", __LINE__, \
-                 u.l, v.l);                                               \
+         printf ("Error sscanf on line %d: %." #n "Lg != %." #n "Lg\n",   \
+                 __LINE__, u.l, v.l);                                     \
          result = 1;                                                      \
        }                                                                  \
       /* printf ("%s %Lg %016Lx %016Lx\n", #val, u.l, u.x[0], u.x[1]); */  \
     }                                                                     \
   while (0)
 
+#define TEST(val) TEST_N (val,30)
+
 #if LDBL_MANT_DIG >= 106
 # if LDBL_MANT_DIG == 106
   TEST (2.22507385850719347803989925739e-308L);
   TEST (2.22507385850719397210554509863e-308L);
   TEST (2.22507385850720088902458687609e-308L);
+
+  /* Verify precision is not lost for long doubles
+     of the form +1.pN,-1.pM.  */
+  TEST_N (3.32306998946228968225951765070082e+35L, 34);
 # endif
   TEST (2.22507385850720138309023271733e-308L);
   TEST (2.22507385850720187715587855858e-308L);
index e9b5803de3696afd7b0fe6fdcb18e3c9129f3e0e..030a2aa98f120fca5b07c6268132831dc365a88e 100644 (file)
    bits (106 for long double) and an integral power of two (MPN
    frexpl). */
 
+
+/* When signs differ, the actual value is the difference between the
+   significant double and the less significant double.  Sometimes a
+   bit can be lost when we borrow from the significant mantissa.  */
+#define EXTRA_INTERNAL_PRECISION (7)
+
 mp_size_t
 __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
                           int *expt, int *is_neg,
@@ -45,10 +51,15 @@ __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
   lo = ((long long) u.d[1].ieee.mantissa0 << 32) | u.d[1].ieee.mantissa1;
   hi = ((long long) u.d[0].ieee.mantissa0 << 32) | u.d[0].ieee.mantissa1;
 
+  /* Hold 7 extra bits of precision in the mantissa.  This allows
+     the normalizing shifts below to prevent losing precision when
+     the signs differ and the exponents are sufficiently far apart.  */
+  lo <<= EXTRA_INTERNAL_PRECISION;
+
   /* If the lower double is not a denormal or zero then set the hidden
      53rd bit.  */
   if (u.d[1].ieee.exponent != 0)
-    lo |= 1ULL << 52;
+    lo |= 1ULL << (52 + EXTRA_INTERNAL_PRECISION);
   else
     lo = lo << 1;
 
@@ -72,12 +83,12 @@ __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
   if (u.d[0].ieee.negative != u.d[1].ieee.negative
       && lo != 0)
     {
-      lo = (1ULL << 53) - lo;
+      lo = (1ULL << (53 + EXTRA_INTERNAL_PRECISION)) - lo;
       if (hi == 0)
        {
          /* we have a borrow from the hidden bit, so shift left 1.  */
-         hi = 0x0ffffffffffffeLL | (lo >> 51);
-         lo = 0x1fffffffffffffLL & (lo << 1);
+         hi = 0x000ffffffffffffeLL | (lo >> (52 + EXTRA_INTERNAL_PRECISION));
+         lo = 0x0fffffffffffffffLL & (lo << 1);
          (*expt)--;
        }
       else
@@ -85,14 +96,14 @@ __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
     }
 #if BITS_PER_MP_LIMB == 32
   /* Combine the mantissas to be contiguous.  */
-  res_ptr[0] = lo;
-  res_ptr[1] = (hi << (53 - 32)) | (lo >> 32);
+  res_ptr[0] = lo >> EXTRA_INTERNAL_PRECISION;
+  res_ptr[1] = (hi << (53 - 32)) | (lo >> (32 + EXTRA_INTERNAL_PRECISION));
   res_ptr[2] = hi >> 11;
   res_ptr[3] = hi >> (32 + 11);
   #define N 4
 #elif BITS_PER_MP_LIMB == 64
   /* Combine the two mantissas to be contiguous.  */
-  res_ptr[0] = (hi << 53) | lo;
+  res_ptr[0] = (hi << 53) | (lo >> EXTRA_INTERNAL_PRECISION);
   res_ptr[1] = hi >> 11;
   #define N 2
 #else