Fix strtod subnormal rounding (bug 30220)

As reported in bug 30220, the implementation of strtod-family
functions has a bug in the following case: the input string would,
with infinite exponent range, take one more bit to represent than is
available in the normal precision of the return type; the value
represented is in the subnormal range; and there are no nonzero bits
in the value, below those that can be represented in subnormal
precision, other than the least significant bit and possibly the
0.5ulp bit.  In this case, round_and_return ends up discarding the
least significant bit.

Fix by saving that bit to merge into more_bits (it can't be merged in
at the time it's computed, because more_bits mustn't include this bit
in the case of after-rounding tininess detection checking if the
result is still subnormal when rounded to normal precision, so merging
this bit into more_bits needs to take place after that check).

Tested for x86_64.
This commit is contained in:
Joseph Myers 2024-08-27 12:41:02 +00:00
parent d73ed2601b
commit 457622c2fa
3 changed files with 386 additions and 0 deletions

View File

@ -222,6 +222,7 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative,
mp_size_t shift = MIN_EXP - 1 - exponent;
bool is_tiny = true;
bool old_half_bit = (round_limb & (((mp_limb_t) 1) << round_bit)) != 0;
more_bits |= (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0;
if (shift == MANT_DIG)
@ -292,6 +293,7 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative,
round_bit = shift - 1;
(void) __mpn_rshift (retval, retval, RETURN_LIMB_SIZE, shift);
}
more_bits |= old_half_bit;
/* This is a hook for the m68k long double format, where the
exponent bias is the same for normalized and denormalized
numbers. */

View File

@ -265,3 +265,15 @@
1.000000000000000000000000000000000385185988877447170611195588516985463707620329643077639047987759113311767578125
1.0000000000000000000000000000000001925929944387235853055977942584927318538101648215388195239938795566558837890625
1.00000000000000000000000000000000009629649721936179265279889712924636592690508241076940976199693977832794189453125
0x30000002222225p-1077
0x0.7fffffffffffeap-1022
0x0.7fffffffffffe9p-1022
0x0.7ffffd4p-126
0x0.7ffffffffffffffd4p-16382
0x0.7ffffffffffffffd4p-16383
0x0.7ffffffffffffffffffffffffffeap-16382
0x0.7000004p-126
0x0.70000000000002p-1022
0x0.70000000000000004p-16382
0x0.70000000000000004p-16383
0x0.70000000000000000000000000002p-16382

View File

@ -15437,4 +15437,376 @@ static const struct test tests[] = {
0x1p+0, false, false,
0x1p+0, false, false,
0x1.0000000000000000000000000001p+0, false, false),
TEST ("0x30000002222225p-1077",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x1.800000111111p-1024, false, true,
0x1.8000001111114p-1024, false, true,
0x1.800000111111p-1024, false, true,
0x1.8000001111114p-1024, false, true,
true,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
true,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
false,
0x1.800000111111p-1024, false, true,
0x1.8000001111114p-1024, false, true,
0x1.800000111111p-1024, false, true,
0x1.8000001111114p-1024, false, true,
true,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false,
0x1.80000011111128p-1024, false, false),
TEST ("0x0.7fffffffffffeap-1022",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
true,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
true,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
false,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
true,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false,
0x1.ffffffffffffa8p-1024, false, false),
TEST ("0x0.7fffffffffffe9p-1022",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
true,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
true,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
false,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
0x1.ffffffffffff8p-1024, false, true,
0x1.ffffffffffffcp-1024, false, true,
true,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false,
0x1.ffffffffffffa4p-1024, false, false),
TEST ("0x0.7ffffd4p-126",
false,
0x1.fffffp-128, false, true,
0x1.fffff8p-128, false, true,
0x1.fffffp-128, false, true,
0x1.fffff8p-128, false, true,
true,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
true,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
true,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
true,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
true,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false,
0x1.fffff5p-128, false, false),
TEST ("0x0.7ffffffffffffffd4p-16382",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0x1.fffffffffffffffp-16384, false, true,
0x1.fffffffffffffff8p-16384, false, true,
0x1.fffffffffffffffp-16384, false, true,
0x1.fffffffffffffff8p-16384, false, true,
false,
0x1.fffffffffffffff4p-16384, false, true,
0x1.fffffffffffffff4p-16384, false, true,
0x1.fffffffffffffff4p-16384, false, true,
0x1.fffffffffffffff8p-16384, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
true,
0x1.fffffffffffffff5p-16384, false, false,
0x1.fffffffffffffff5p-16384, false, false,
0x1.fffffffffffffff5p-16384, false, false,
0x1.fffffffffffffff5p-16384, false, false),
TEST ("0x0.7ffffffffffffffd4p-16383",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0xf.ffffffffffffff8p-16388, false, true,
0xf.ffffffffffffff8p-16388, false, true,
0xf.ffffffffffffff8p-16388, false, true,
0x1p-16384, false, true,
false,
0xf.ffffffffffffff8p-16388, false, true,
0xf.ffffffffffffffcp-16388, false, true,
0xf.ffffffffffffff8p-16388, false, true,
0xf.ffffffffffffffcp-16388, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
true,
0xf.ffffffffffffffa8p-16388, false, false,
0xf.ffffffffffffffa8p-16388, false, false,
0xf.ffffffffffffffa8p-16388, false, false,
0xf.ffffffffffffffa8p-16388, false, false),
TEST ("0x0.7ffffffffffffffffffffffffffeap-16382",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0x1.fffffffffffffff8p-16384, false, true,
0x2p-16384, false, true,
0x1.fffffffffffffff8p-16384, false, true,
0x2p-16384, false, true,
false,
0x1.fffffffffffffffcp-16384, false, true,
0x2p-16384, false, true,
0x1.fffffffffffffffcp-16384, false, true,
0x2p-16384, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0x1.fffffffffffffffffffffffffff8p-16384, false, true,
0x1.fffffffffffffffffffffffffffcp-16384, false, true,
0x1.fffffffffffffffffffffffffff8p-16384, false, true,
0x1.fffffffffffffffffffffffffffcp-16384, false, true),
TEST ("0x0.7000004p-126",
false,
0x1.cp-128, false, true,
0x1.cp-128, false, true,
0x1.cp-128, false, true,
0x1.c00008p-128, false, true,
true,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
true,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
true,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
true,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
true,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false,
0x1.c00001p-128, false, false),
TEST ("0x0.70000000000002p-1022",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x1.cp-1024, false, true,
0x1.cp-1024, false, true,
0x1.cp-1024, false, true,
0x1.c000000000004p-1024, false, true,
true,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
true,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
false,
0x1.cp-1024, false, true,
0x1.cp-1024, false, true,
0x1.cp-1024, false, true,
0x1.c000000000004p-1024, false, true,
true,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false,
0x1.c0000000000008p-1024, false, false),
TEST ("0x0.70000000000000004p-16382",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.c000000000000008p-16384, false, true,
false,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.c000000000000004p-16384, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
true,
0x1.c000000000000001p-16384, false, false,
0x1.c000000000000001p-16384, false, false,
0x1.c000000000000001p-16384, false, false,
0x1.c000000000000001p-16384, false, false),
TEST ("0x0.70000000000000004p-16383",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0xep-16388, false, true,
0xep-16388, false, true,
0xep-16388, false, true,
0xe.000000000000008p-16388, false, true,
false,
0xep-16388, false, true,
0xep-16388, false, true,
0xep-16388, false, true,
0xe.000000000000004p-16388, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
true,
0xe.0000000000000008p-16388, false, false,
0xe.0000000000000008p-16388, false, false,
0xe.0000000000000008p-16388, false, false,
0xe.0000000000000008p-16388, false, false),
TEST ("0x0.70000000000000000000000000002p-16382",
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x8p-152, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.c000000000000008p-16384, false, true,
false,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.c000000000000004p-16384, false, true,
false,
0x0p+0, false, true,
0x0p+0, false, true,
0x0p+0, false, true,
0x4p-1076, false, true,
false,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.cp-16384, false, true,
0x1.c000000000000000000000000004p-16384, false, true),
};