Fix ldbl-128ibm lrintl, lroundl missing "invalid" exceptions (bug 22690).

The ldbl-128ibm implementations of lrintl and lroundl are missing
"invalid" exceptions for certain overflow cases when compiled with GCC
8.  The cause of this is after-the-fact integer overflow checks that
fail when the compiler optimizes on the basis of integer overflow
being undefined; GCC 8 must be able to detect new cases of
undefinedness here.

Failure: lrint (-0x80000001p0): Exception "Invalid operation" not set
Failure: lrint_downward (-0x80000001p0): Exception "Invalid operation" not set
Failure: lrint_towardzero (-0x80000001p0): Exception "Invalid operation" not set
Failure: lrint_upward (-0x80000001p0): Exception "Invalid operation" not set

Failure: lround (-0x80000001p0): Exception "Invalid operation" not set
Failure: lround_downward (-0x80000001p0): Exception "Invalid operation" not set
Failure: lround_towardzero (-0x80000001p0): Exception "Invalid operation" not set
Failure: lround_upward (-0x80000001p0): Exception "Invalid operation" not set

(Tested that these failures occur before the patch for powerpc
soft-float, but the issue applies in principle for hard-float as well,
whether or not the particular optimizations in fact occur there at
present.)

This patch fixes the bug by ensuring the additions / subtractions in
question cast arguments to unsigned long int, or use 1UL as a constant
argument, so that the arithmetic occurs in an unsigned type with the
result then converted back to a signed type.

Tested for powerpc (soft-float).

	[BZ #22690]
	* sysdeps/ieee754/ldbl-128ibm/s_lrintl.c (__lrintl): Use unsigned
	long int for arguments of possibly overflowing addition or
	subtraction.
	* sysdeps/ieee754/ldbl-128ibm/s_lroundl.c (__lroundl): Likewise.
This commit is contained in:
Joseph Myers 2018-01-10 00:02:35 +00:00
parent b2584ac2a4
commit 1272748886
3 changed files with 20 additions and 12 deletions

View File

@ -1,3 +1,11 @@
2018-01-10 Joseph Myers <joseph@codesourcery.com>
[BZ #22690]
* sysdeps/ieee754/ldbl-128ibm/s_lrintl.c (__lrintl): Use unsigned
long int for arguments of possibly overflowing addition or
subtraction.
* sysdeps/ieee754/ldbl-128ibm/s_lroundl.c (__lroundl): Likewise.
2018-01-09 Joseph Myers <joseph@codesourcery.com> 2018-01-09 Joseph Myers <joseph@codesourcery.com>
[BZ #22688] [BZ #22688]

View File

@ -84,7 +84,7 @@ __lrintl (long double x)
/* Peg at max/min values, assuming that the above conversions do so. /* Peg at max/min values, assuming that the above conversions do so.
Strictly speaking, we can return anything for values that overflow, Strictly speaking, we can return anything for values that overflow,
but this is more useful. */ but this is more useful. */
res = hi + lo; res = (long int) ((unsigned long int) hi + (unsigned long int) lo);
/* This is just sign(hi) == sign(lo) && sign(res) != sign(hi). */ /* This is just sign(hi) == sign(lo) && sign(res) != sign(hi). */
if (__glibc_unlikely (((~(hi ^ lo) & (res ^ hi)) < 0))) if (__glibc_unlikely (((~(hi ^ lo) & (res ^ hi)) < 0)))
@ -105,27 +105,27 @@ __lrintl (long double x)
return res; return res;
if (xh < 0.0) if (xh < 0.0)
res -= 1; res -= 1UL;
else else
res += 1; res += 1UL;
break; break;
case FE_TOWARDZERO: case FE_TOWARDZERO:
if (res > 0 && (xh < 0.0 || (xh == 0.0 && xl < 0.0))) if (res > 0 && (xh < 0.0 || (xh == 0.0 && xl < 0.0)))
res -= 1; res -= 1UL;
else if (res < 0 && (xh > 0.0 || (xh == 0.0 && xl > 0.0))) else if (res < 0 && (xh > 0.0 || (xh == 0.0 && xl > 0.0)))
res += 1; res += 1UL;
return res; return res;
break; break;
case FE_UPWARD: case FE_UPWARD:
if (xh > 0.0 || (xh == 0.0 && xl > 0.0)) if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
res += 1; res += 1UL;
break; break;
case FE_DOWNWARD: case FE_DOWNWARD:
if (xh < 0.0 || (xh == 0.0 && xl < 0.0)) if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
res -= 1; res -= 1UL;
break; break;
} }

View File

@ -80,7 +80,7 @@ __lroundl (long double x)
/* Peg at max/min values, assuming that the above conversions do so. /* Peg at max/min values, assuming that the above conversions do so.
Strictly speaking, we can return anything for values that overflow, Strictly speaking, we can return anything for values that overflow,
but this is more useful. */ but this is more useful. */
res = hi + lo; res = (long int) ((unsigned long int) hi + (unsigned long int) lo);
/* This is just sign(hi) == sign(lo) && sign(res) != sign(hi). */ /* This is just sign(hi) == sign(lo) && sign(res) != sign(hi). */
if (__glibc_unlikely (((~(hi ^ lo) & (res ^ hi)) < 0))) if (__glibc_unlikely (((~(hi ^ lo) & (res ^ hi)) < 0)))
@ -92,21 +92,21 @@ __lroundl (long double x)
hi = res; hi = res;
if (xh > 0.5) if (xh > 0.5)
{ {
res += 1; res += 1UL;
} }
else if (xh == 0.5) else if (xh == 0.5)
{ {
if (xl > 0.0 || (xl == 0.0 && res >= 0)) if (xl > 0.0 || (xl == 0.0 && res >= 0))
res += 1; res += 1UL;
} }
else if (-xh > 0.5) else if (-xh > 0.5)
{ {
res -= 1; res -= 1UL;
} }
else if (-xh == 0.5) else if (-xh == 0.5)
{ {
if (xl < 0.0 || (xl == 0.0 && res <= 0)) if (xl < 0.0 || (xl == 0.0 && res <= 0))
res -= 1; res -= 1UL;
} }
if (__glibc_unlikely (((~(hi ^ (res - hi)) & (res ^ hi)) < 0))) if (__glibc_unlikely (((~(hi ^ (res - hi)) & (res ^ hi)) < 0)))