Skip to content

Commit

Permalink
Increase internal precision of ldbl-128ibm decimal printf [BZ #19853]
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Paul E. Murphy committed Mar 31, 2016
1 parent 8305663 commit 37a4c70
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 11 deletions.
14 changes: 14 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
2016-03-31 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-03-31 H.J. Lu <hongjiu.lu@intel.com>

[BZ #19881]
Expand Down
14 changes: 10 additions & 4 deletions stdio-common/tst-sprintf3.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,37 @@ 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); \
result = 1; \
} \
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);
Expand Down
25 changes: 18 additions & 7 deletions sysdeps/ieee754/ldbl-128ibm/ldbl2mpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;

Expand All @@ -72,27 +83,27 @@ __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
hi--;
}
#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
Expand Down

0 comments on commit 37a4c70

Please sign in to comment.