[PATCH v2 2/2] Simplify powl computation for small integral y [BZ #33411]

H.J. Lu hjl.tools@gmail.com
Wed Oct 29 22:53:53 GMT 2025


On Tue, Oct 21, 2025 at 8:22 PM Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
>
> The powl implementation for x86_64 ends up multiplying X once more than
> necessary and then throwing away that result.  This results in an
> overflow flag being set in cases where there is no overflow.
>
> Simplify the relevant portion by special casing the -3 to 3 range and
> simply multiplying repetitively.
>
> Resolves: BZ #33411
> Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
> ---
>  math/test-powl.c            | 22 +++++++++++++--
>  sysdeps/x86_64/fpu/e_powl.S | 56 +++++++++++++++++++------------------
>  2 files changed, 49 insertions(+), 29 deletions(-)
>
> diff --git a/math/test-powl.c b/math/test-powl.c
> index 2c716043b8..25253d51d7 100644
> --- a/math/test-powl.c
> +++ b/math/test-powl.c
> @@ -16,10 +16,11 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */
>
> -#include <stdio.h>
> -#include <math.h>
> +#include <fenv.h>
>  #include <float.h>
>  #include <ieee754.h>
> +#include <math.h>
> +#include <stdio.h>
>  #include <support/check.h>
>
>  /* Smoke test for bz12775, which discovered a typo in the x86_64 powl
> @@ -46,10 +47,27 @@ do_test_bz12775 (void)
>  #endif
>  }
>
> +/* Make sure that a long double ^ 1 does not set the overflow flag.  For
> +   context see BZ #33411.  */
> +static void
> +do_test_overflow (void)
> +{
> +    long double x = 1.090748135619415929463e+2466L;
> +
> +    feclearexcept( FE_ALL_EXCEPT );
> +
> +    long double res = powl (x, 1.L);
> +    int flags = fetestexcept( FE_ALL_EXCEPT );
> +
> +    TEST_VERIFY (res == x);
> +    TEST_COMPARE (flags & FE_OVERFLOW, 0);
> +}
> +
>  static int
>  do_test (void)
>  {
>    do_test_bz12775 ();
> +  do_test_overflow ();
>
>    return 0;
>  }
> diff --git a/sysdeps/x86_64/fpu/e_powl.S b/sysdeps/x86_64/fpu/e_powl.S
> index 620ef765a7..39f77480e8 100644
> --- a/sysdeps/x86_64/fpu/e_powl.S
> +++ b/sysdeps/x86_64/fpu/e_powl.S
> @@ -144,39 +144,41 @@ ENTRY(__ieee754_powl)
>         fcomip  %st(1), %st     // 4 : y : x
>         fstp    %st(0)          // y : x
>         jnc     3f
> -       mov     -8(%rsp),%eax
> -       mov     -4(%rsp),%edx
> -       orl     $0, %edx
> +
> +       /* Here onwards, it's just integral y in range [-3, 3].  */
> +       movq    -8(%rsp),%rax
> +       orq     $0, %rax
>         fstp    %st(0)          // x
>         jns     4f              // y >= 0, jump
>         fdivrl  MO(one)         // 1/x          (now referred to as x)
> -       negl    %eax
> -       adcl    $0, %edx
> -       negl    %edx
> +       negq    %rax
>  4:     fldl    MO(one)         // 1 : x
> -       fxch
>
> -       /* If y is even, take the absolute value of x.  Otherwise,
> -          ensure all intermediate values that might overflow have the
> -          sign of x.  */
> +       /* y range is further reduced to [0, 3].  Simply walk through the
> +          options.  First up, 0 and 1.  */
> +       test    %eax, %eax
> +       jz      6f
> +       fxch                    // x : 1
> +       subl    $1, %eax
> +       jz      6f
> +
> +       /* Finally, y == 2 and 3.  For y == 3 we do |x| * x * |x| because x * x
> +          and |x| * |x| decay faster towards infinity compared to x * |x|.  */
> +       fld     %st             // x : x : 1
> +       fabs                    // |x| : x : 1
> +       fxch                    // x : |x| : 1
> +       fld     %st(1)          // |x| : x : |x| : 1
>         testb   $1, %al
> -       jnz     6f
> -       fabs
> -
> -6:     shrdl   $1, %edx, %eax
> -       jnc     5f
> -       fxch
> -       fabs
> -       fmul    %st(1)          // x : ST*x
> -       fxch
> -5:     fld     %st             // x : x : ST*x
> -       fabs                    // |x| : x : ST*x
> -       fmulp                   // |x|*x : ST*x
> -       shrl    $1, %edx
> -       movl    %eax, %ecx
> -       orl     %edx, %ecx
> -       jnz     6b
> -       fstp    %st(0)          // ST*x
> +       jz      7f
> +       fmulp   %st(2)          // x : |x| * |x| : 1
> +       fstp    %st(0)          // |x| * |x| : 1
> +       jmp     6f
> +7:     fmulp                   // |x| * x : |x| : 1
> +       fmulp                   // |x| * x * |x| : 1
> +
> +       /* We come here with the stack as RES : <something>, so pop off
> +          <something>.  */
> +6:     fstp    %st(1)
>         LDBL_CHECK_FORCE_UFLOW_NONNAN
>         ret
>
> --
> 2.51.0
>

This caused:

FAIL: math/test-float64x-pow
original exit status 1
testing _Float64x (without inline functions)
Failure: pow (0x1p+8192, 0x1p+0): Exception "Overflow" set
Failure: pow_downward (0x1p+8192, 0x1p+0): Exception "Overflow" set
Failure: pow_towardzero (0x1p+8192, 0x1p+0): Exception "Overflow" set
Failure: pow_upward (0x1p+8192, 0x1p+0): Exception "Overflow" set

on i686 with -O2 -march=i686.

-- 
H.J.


More information about the Libc-alpha mailing list