math: Fix UB in ldbl-128ibm llrintl

This commit is contained in:
Adhemerval Zanella 2025-05-08 12:20:21 +00:00
parent 5ae9c869b0
commit 673776c7be
1 changed files with 19 additions and 10 deletions

View File

@ -23,13 +23,20 @@
#include <math_ldbl_opt.h> #include <math_ldbl_opt.h>
#include <float.h> #include <float.h>
#include <ieee754.h> #include <ieee754.h>
#include <intprops.h>
static inline bool
check_sign (unsigned long int v1, unsigned long int v2)
{
enum { sign_shift = sizeof (unsigned long long int) * CHAR_BIT - 1 };
return (v1 & (1UL << sign_shift)) == (v2 & (1UL << sign_shift));
}
long long long long
__llrintl (long double x) __llrintl (long double x)
{ {
double xh, xl; double xh, xl;
long long res, hi, lo; long long res, hi, lo, tmp;
int save_round; int save_round;
ldbl_unpack (x, &xh, &xl); ldbl_unpack (x, &xh, &xl);
@ -70,10 +77,11 @@ __llrintl (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; INT_ADD_WRAPV (hi, lo, &res);
/* 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)))
if (__glibc_unlikely (check_sign (hi, lo) && !check_sign (res, hi)))
goto overflow; goto overflow;
xh -= lo; xh -= lo;
@ -91,31 +99,32 @@ __llrintl (long double x)
return res; return res;
if (xh < 0.0) if (xh < 0.0)
res -= 1; INT_SUBTRACT_WRAPV (res, 1, &res);
else else
res += 1; INT_ADD_WRAPV (res, 1, &res);
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; INT_SUBTRACT_WRAPV (res, 1, &res);
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; INT_ADD_WRAPV (res, 1, &res);
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; INT_ADD_WRAPV (res, 1, &res);
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; INT_SUBTRACT_WRAPV (res, 1, &res);
break; break;
} }
if (__glibc_unlikely (((~(hi ^ (res - hi)) & (res ^ hi)) < 0))) INT_SUBTRACT_WRAPV (res, hi, &tmp);
if (__glibc_unlikely (check_sign (hi, tmp) && !check_sign (res, hi)))
goto overflow; goto overflow;
return res; return res;