summary refs log tree commit diff
path: root/sysdeps
diff options
context:
space:
mode:
authorPaul E. Murphy <murphyp@linux.vnet.ibm.com>2016-02-29 13:27:36 -0600
committerPaul E. Murphy <murphyp@linux.vnet.ibm.com>2016-03-31 12:14:33 -0500
commit37a4c70bd4c5c74ac562072e450dc02e8cb4c150 (patch)
treec9a631e747dcb7ca4213c93460d7e45d153a0771 /sysdeps
parent830566307f038387ca0af3fd327706a8d1a2f595 (diff)
downloadglibc-37a4c70bd4c5c74ac562072e450dc02e8cb4c150.tar.gz
glibc-37a4c70bd4c5c74ac562072e450dc02e8cb4c150.tar.xz
glibc-37a4c70bd4c5c74ac562072e450dc02e8cb4c150.zip
Increase internal precision of ldbl-128ibm decimal printf [BZ #19853]
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.
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c25
1 files changed, 18 insertions, 7 deletions
diff --git a/sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c b/sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c
index 4f550ef47c..30d9bc3443 100644
--- a/sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c
+++ b/sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c
@@ -28,6 +28,12 @@
    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