diff options
author | Paul E. Murphy <murphyp@linux.vnet.ibm.com> | 2016-02-29 13:27:36 -0600 |
---|---|---|
committer | Paul E. Murphy <murphyp@linux.vnet.ibm.com> | 2016-03-31 12:14:33 -0500 |
commit | 37a4c70bd4c5c74ac562072e450dc02e8cb4c150 (patch) | |
tree | c9a631e747dcb7ca4213c93460d7e45d153a0771 /sysdeps/ieee754/ldbl-128ibm | |
parent | 830566307f038387ca0af3fd327706a8d1a2f595 (diff) | |
download | glibc-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/ieee754/ldbl-128ibm')
-rw-r--r-- | sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c | 25 |
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 |