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

H.J. Lu hjl.tools@gmail.com
Wed Oct 29 23:13:51 GMT 2025


On Thu, Oct 30, 2025 at 6:53 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> 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.

I opened:

https://sourceware.org/bugzilla/show_bug.cgi?id=33586

-- 
H.J.


More information about the Libc-alpha mailing list