1 /* mpfr_pow_ui-- compute the power of a floating-point
4 Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
5 Contributed by the Arenaire and Cacao projects, INRIA.
7 This file is part of the GNU MPFR Library.
9 The GNU MPFR Library is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or (at your
12 option) any later version.
14 The GNU MPFR Library is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17 License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with the GNU MPFR Library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
22 MA 02110-1301, USA. */
24 #define MPFR_NEED_LONGLONG_H
25 #include "mpfr-impl.h"
27 /* sets y to x^n, and return 0 if exact, non-zero otherwise */
29 mpfr_pow_ui (mpfr_ptr y
, mpfr_srcptr x
, unsigned long int n
, mp_rnd_t rnd
)
36 MPFR_SAVE_EXPO_DECL (expo
);
38 MPFR_BLOCK_DECL (flags
);
40 MPFR_LOG_FUNC (("x[%#R]=%R n=%lu rnd=%d", x
, x
, n
, rnd
),
41 ("y[%#R]=%R inexact=%d", y
, y
, inexact
));
43 /* x^0 = 1 for any x, even a NaN */
44 if (MPFR_UNLIKELY (n
== 0))
45 return mpfr_set_ui (y
, 1, rnd
);
47 if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x
)))
54 else if (MPFR_IS_INF (x
))
56 /* Inf^n = Inf, (-Inf)^n = Inf for n even, -Inf for n odd */
57 if (MPFR_IS_NEG (x
) && (n
& 1) == 1)
66 MPFR_ASSERTD (MPFR_IS_ZERO (x
));
67 /* 0^n = 0 for any n */
69 if (MPFR_IS_POS (x
) || (n
& 1) == 0)
76 else if (MPFR_UNLIKELY (n
<= 2))
80 return mpfr_set (y
, x
, rnd
);
83 return mpfr_sqr (y
, x
, rnd
);
86 /* Augment exponent range */
87 MPFR_SAVE_EXPO_MARK (expo
);
89 /* setup initial precision */
90 prec
= MPFR_PREC (y
) + 3 + BITS_PER_MP_LIMB
91 + MPFR_INT_CEIL_LOG2 (MPFR_PREC (y
));
92 mpfr_init2 (res
, prec
);
94 rnd1
= MPFR_IS_POS (x
) ? GMP_RNDU
: GMP_RNDD
; /* away */
96 MPFR_ZIV_INIT (loop
, prec
);
101 for (m
= n
, i
= 0; m
; i
++, m
>>= 1)
103 /* now 2^(i-1) <= n < 2^i */
104 MPFR_ASSERTD (prec
> (mpfr_prec_t
) i
);
105 err
= prec
- 1 - (mpfr_prec_t
) i
;
106 /* First step: compute square from x */
108 inexact
= mpfr_mul (res
, x
, x
, GMP_RNDU
);
109 MPFR_ASSERTD (i
>= 2);
110 if (n
& (1UL << (i
-2)))
111 inexact
|= mpfr_mul (res
, res
, x
, rnd1
);
112 for (i
-= 3; i
>= 0 && !MPFR_BLOCK_EXCEP
; i
--)
114 inexact
|= mpfr_mul (res
, res
, res
, GMP_RNDU
);
116 inexact
|= mpfr_mul (res
, res
, x
, rnd1
);
118 /* let r(n) be the number of roundings: we have r(2)=1, r(3)=2,
119 and r(2n)=2r(n)+1, r(2n+1)=2r(n)+2, thus r(n)=n-1.
120 Using Higham's method, to each rounding corresponds a factor
121 (1-theta) with 0 <= theta <= 2^(1-p), thus at the end the
122 absolute error is bounded by (n-1)*2^(1-p)*res <= 2*(n-1)*ulp(res)
123 since 2^(-p)*x <= ulp(x). Since n < 2^i, this gives a maximal
124 error of 2^(1+i)*ulp(res).
126 if (MPFR_LIKELY (inexact
== 0
127 || MPFR_OVERFLOW (flags
) || MPFR_UNDERFLOW (flags
)
128 || MPFR_CAN_ROUND (res
, err
, MPFR_PREC (y
), rnd
)))
130 /* Actualisation of the precision */
131 MPFR_ZIV_NEXT (loop
, prec
);
132 mpfr_set_prec (res
, prec
);
134 MPFR_ZIV_FREE (loop
);
136 if (MPFR_UNLIKELY (MPFR_OVERFLOW (flags
) || MPFR_UNDERFLOW (flags
)))
140 /* Internal overflow or underflow. However the approximation error has
141 * not been taken into account. So, let's solve this problem by using
142 * mpfr_pow_z, which can handle it. This case could be improved in the
143 * future, without having to use mpfr_pow_z.
145 MPFR_LOG_MSG (("Internal overflow or underflow,"
146 " let's use mpfr_pow_z.\n", 0));
148 MPFR_SAVE_EXPO_FREE (expo
);
151 inexact
= mpfr_pow_z (y
, x
, z
, rnd
);
156 inexact
= mpfr_set (y
, res
, rnd
);
159 MPFR_SAVE_EXPO_FREE (expo
);
160 return mpfr_check_range (y
, inexact
, rnd
);