mirror of git://sourceware.org/git/glibc.git
92 lines
2.6 KiB
C
92 lines
2.6 KiB
C
/* Return X^Y for integer Y.
|
|
Copyright (C) 2025 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#include <errno.h>
|
|
#include <fenv_private.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <math-barriers.h>
|
|
#include <math-narrow-eval.h>
|
|
#include <math_private.h>
|
|
#include <stdlib.h>
|
|
|
|
FLOAT
|
|
M_DECL_FUNC (__pown) (FLOAT x, long long int y)
|
|
{
|
|
FLOAT ret;
|
|
#if M_MANT_DIG >= LLONG_WIDTH - 1
|
|
ret = M_SUF (__ieee754_pow) (x, y);
|
|
#else
|
|
if ((y <= 1LL << M_MANT_DIG
|
|
&& y >= -(1LL << M_MANT_DIG))
|
|
|| y == LLONG_MIN)
|
|
ret = M_SUF (__ieee754_pow) (x, y);
|
|
else
|
|
{
|
|
{
|
|
/* To avoid problems with overflow and underflow from
|
|
intermediate computations occurring in the wrong directed
|
|
rounding mode (when X is negative and Y is odd), do these
|
|
computations in round-to-nearest mode and correct
|
|
overflowing and underflowing results afterwards. */
|
|
M_SET_RESTORE_ROUND (FE_TONEAREST);
|
|
ret = M_LIT (1.0);
|
|
while (y != 0)
|
|
{
|
|
long long int absy = llabs (y);
|
|
int bits = LLONG_WIDTH - __builtin_clzll (absy);
|
|
if (bits <= M_MANT_DIG)
|
|
{
|
|
ret *= M_SUF (__ieee754_pow) (x, y);
|
|
y = 0;
|
|
}
|
|
else
|
|
{
|
|
long long int absy_high
|
|
= absy & (((1ULL << M_MANT_DIG) - 1) << (bits - M_MANT_DIG));
|
|
long long int absy_low = absy - absy_high;
|
|
long long int y_high = y < 0 ? -absy_high : absy_high;
|
|
ret *= M_SUF (__ieee754_pow) (x, y_high);
|
|
y = y < 0 ? -absy_low : absy_low;
|
|
}
|
|
}
|
|
ret = math_narrow_eval (ret);
|
|
math_force_eval (ret);
|
|
}
|
|
if (isfinite (x) && x != M_LIT (0.0))
|
|
{
|
|
if (isinf (ret))
|
|
ret = math_narrow_eval (M_COPYSIGN (M_MAX, ret) * M_MAX);
|
|
else if (ret == M_LIT (0.0))
|
|
ret = math_narrow_eval (M_COPYSIGN (M_MIN, ret) * M_MIN);
|
|
}
|
|
}
|
|
#endif
|
|
if (!isfinite (ret))
|
|
{
|
|
if (isfinite (x))
|
|
__set_errno (ERANGE);
|
|
}
|
|
else if (ret == 0
|
|
&& isfinite (x)
|
|
&& x != 0)
|
|
__set_errno (ERANGE);
|
|
return ret;
|
|
}
|
|
declare_mgen_alias (__pown, pown);
|