From 9c7ca2ba1020fadba5939b3792f1021843c1c808 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 20 Aug 2023 13:23:11 -0700 Subject: [PATCH] ldexp: fix INT_MIN infloop * lib/ldexp.c (FUNC): Instead of converting EXP to unsigned, work on it directly. This simplifies the code and avoids an infinite loop when EXP == INT_MIN. * modules/ldexp, modules/ldexpl: Depend on stdbool. * tests/test-ldexp.h: Include for INT_MIN. (test_function): Test for infloop. --- ChangeLog | 10 ++++++++++ lib/ldexp.c | 39 ++++++++++----------------------------- modules/ldexp | 1 + modules/ldexpl | 1 + tests/test-ldexp.h | 3 +++ 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index bad59ebcf6..764176466f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2023-08-20 Paul Eggert + + ldexp: fix INT_MIN infloop + * lib/ldexp.c (FUNC): Instead of converting EXP to unsigned, + work on it directly. This simplifies the code and avoids + an infinite loop when EXP == INT_MIN. + * modules/ldexp, modules/ldexpl: Depend on stdbool. + * tests/test-ldexp.h: Include for INT_MIN. + (test_function): Test for infloop. + 2023-08-20 Bruno Haible ldexp: Fix compilation error in C++ mode. diff --git a/lib/ldexp.c b/lib/ldexp.c index dd09ca3814..844fd11f53 100644 --- a/lib/ldexp.c +++ b/lib/ldexp.c @@ -57,38 +57,19 @@ FUNC (DOUBLE x, int exp) BEGIN_ROUNDING (); /* Check for zero, nan and infinity. */ - if (!(ISNAN (x) || x + x == x)) + if (!(ISNAN (x) || x + x == x || exp == 0)) { - unsigned int uexp; - DOUBLE factor; + bool negexp = exp < 0; + DOUBLE factor = negexp ? L_(0.5) : L_(2.0); - if (exp < 0) + while (true) { - /* Avoid signed integer overflow when exp == INT_MIN. */ - uexp = (unsigned int) (-1 - exp) + 1; - factor = L_(0.5); - } - else - { - uexp = exp; - factor = L_(2.0); - } - - if (uexp > 0) - { - unsigned int bit; - - for (bit = 1;;) - { - /* Invariant: Here bit = 2^i, factor = 2^-2^i or = 2^2^i, - and bit <= uexp. */ - if (uexp & bit) - x *= factor; - bit <<= 1; - if (bit > uexp) - break; - factor = factor * factor; - } + if (exp & 1) + x *= factor; + exp = (exp + negexp) >> 1; + if (exp == 0) + break; + factor = factor * factor; } } diff --git a/modules/ldexp b/modules/ldexp index 2f55db6416..528efaad77 100644 --- a/modules/ldexp +++ b/modules/ldexp @@ -8,6 +8,7 @@ m4/ldexp.m4 Depends-on: math isnand [test $REPLACE_LDEXP = 1] +stdbool [test $REPLACE_LDEXP = 1] configure.ac: gl_FUNC_LDEXP diff --git a/modules/ldexpl b/modules/ldexpl index 515ce713cd..622cad6ef5 100644 --- a/modules/ldexpl +++ b/modules/ldexpl @@ -12,6 +12,7 @@ extensions ldexp [{ test $HAVE_DECL_LDEXPL = 0 || test $gl_func_ldexpl = no; } && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1] isnanl [{ test $HAVE_DECL_LDEXPL = 0 || test $gl_func_ldexpl = no; } && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0] fpucw [{ test $HAVE_DECL_LDEXPL = 0 || test $gl_func_ldexpl = no; } && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0] +stdbool [{ test $HAVE_DECL_LDEXPL = 0 || test $gl_func_ldexpl = no; } && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0] configure.ac: gl_FUNC_LDEXPL diff --git a/tests/test-ldexp.h b/tests/test-ldexp.h index b1aac4f325..5252b1424c 100644 --- a/tests/test-ldexp.h +++ b/tests/test-ldexp.h @@ -16,6 +16,8 @@ /* Written by Bruno Haible , 2007, 2010. */ +#include + static void test_function (void) { @@ -99,6 +101,7 @@ test_function (void) ASSERT (y == expected); } y = LDEXP (x, -5); ASSERT (y == x * 0.03125L); + y = LDEXP (x, INT_MIN); ASSERT (y == 0); } for (i = 1, x = L_(1.73205); i >= MIN_EXP; i--, x *= L_(0.5)) { -- 2.39.5