1use core::num::FpCategory;
11
12use super::bkm::{bkm_e, bkm_l};
13use super::exp::approx_exp;
14use super::log::approx_ln;
15use super::{BigUInt, Float512, HiLo, Parity};
16use crate::{
17 abs_bits, exp, f256, norm_signif_exp, EMAX, EMIN, FRACTION_BITS,
18};
19
20enum Lim {
21 Ok,
22 Overflow,
23 Underflow,
24}
25
26impl Lim {
27 #[inline(always)]
29 #[allow(clippy::cast_possible_wrap)]
30 fn powi(x: &f256, n: i32) -> Self {
31 let (m, mut e) = norm_signif_exp(&abs_bits(x));
32 const LOWER_LIM: i32 = -EMAX - 1 - FRACTION_BITS as i32;
37 const UPPER_LIM: i32 = EMAX + 1;
38 let e_times_n = e.saturating_mul(n);
39 match e_times_n {
40 ..=LOWER_LIM => Self::Underflow,
41 UPPER_LIM.. => Self::Overflow,
42 _ => Self::Ok,
43 }
44 }
45
46 #[inline(always)]
48 fn powf(x: &f256, y: &f256) -> Self {
49 match i32::try_from(&y.trunc()) {
50 Ok(n) => Self::powi(x, n),
51 Err(_) => Self::powi(x, [i32::MAX, i32::MIN][y.sign() as usize]),
52 }
53 }
54}
55
56#[inline(always)]
58fn powi(x: &f256, mut n: i32) -> f256 {
59 debug_assert!(x.is_finite() && !x.eq_zero());
60 debug_assert!(n.abs() > 1);
61 let s = x.sign() * ((n.abs() % 2) == 1) as u32;
63 match Lim::powi(x, n) {
64 Lim::Overflow => [f256::INFINITY, f256::NEG_INFINITY][s as usize],
65 Lim::Underflow => [f256::ZERO, f256::NEG_ZERO][s as usize],
66 _ => {
67 f256::from(&Float512::from(x).powi(n))
69 }
70 }
71}
72
73pub(crate) fn approx_powf(mut x: Float512, mut y: Float512) -> Float512 {
74 debug_assert!(y.abs() < Float512::from(i32::MAX));
75 if y.signum() == -1 {
76 x = x.recip();
77 y.flip_sign();
78 }
79 let a = y.trunc();
85 let b = y - a;
87 let mut a = i32::try_from(&a).unwrap();
89 let lnx = approx_ln(&x);
91 let w = b * lnx;
93 let ew = approx_exp(&w);
95 x.powi(a) * ew
97}
98
99#[inline(always)]
101fn powf(x: &f256, y: &f256) -> f256 {
102 debug_assert!(x.is_finite() && !x.eq_zero());
103 debug_assert!(y.is_finite() && !y.eq_zero());
104 let x_sign = x.sign();
105 if let Ok(n) = i32::try_from(y) {
106 let s: usize = match (x_sign, (n % 2) == 1) {
107 (0, _) | (1, false) => 0,
108 (1, true) => 1,
109 _ => unreachable!(),
110 };
111 return match Lim::powi(x, n) {
112 Lim::Overflow => [f256::INFINITY, f256::NEG_INFINITY][s],
113 Lim::Underflow => [f256::ZERO, f256::NEG_ZERO][s],
114 _ => powi(x, n),
115 };
116 };
117 if let Some(p) = y.parity() {
118 let s = match (x_sign, p) {
120 (0, _) | (1, Parity::Even) => 0,
121 (1, Parity::Odd) => 1,
122 _ => unreachable!(),
123 };
124 return match Lim::powi(x, [i32::MAX, i32::MIN][y.sign() as usize]) {
125 Lim::Overflow => [f256::INFINITY, f256::NEG_INFINITY][s],
126 Lim::Underflow => [f256::ZERO, f256::NEG_ZERO][s],
127 _ => unreachable!(),
128 };
129 }
130 if x_sign == 1 {
132 return f256::NAN;
134 };
135 match Lim::powf(x, y) {
136 Lim::Overflow => {
137 [f256::INFINITY, f256::NEG_INFINITY][x_sign as usize]
138 }
139 Lim::Underflow => [f256::ZERO, f256::NEG_ZERO][x_sign as usize],
140 _ => {
141 f256::from(&approx_powf(Float512::from(x), Float512::from(y)))
143 }
144 }
145}
146
147impl f256 {
148 #[must_use]
150 pub fn powi(&self, n: i32) -> Self {
151 if n == 0 || *self == Self::ONE {
154 return Self::ONE;
155 }
156 if n == 1 {
158 return *self;
159 }
160 if n == -1 {
162 return self.recip();
163 }
164 if self.is_special() {
167 if self.is_nan() {
169 return Self::NAN;
170 }
171 if self.eq_zero() {
174 return [Self::ZERO, Self::INFINITY][(n < 0) as usize];
175 }
176 if self.is_infinite() {
183 match (self.sign(), n.signum()) {
184 (0, 1) => return Self::INFINITY,
185 (0, -1) => return Self::ZERO,
186 (1, 1) => {
187 return [Self::INFINITY, Self::NEG_INFINITY]
188 [(n & 1 == 1) as usize]
189 }
190 (1, -1) => {
191 return [Self::ZERO, Self::NEG_ZERO]
192 [(n & 1 == 1) as usize]
193 }
194 _ => unreachable!(),
195 }
196 }
197 }
198 powi(self, n)
200 }
201
202 #[must_use]
204 pub fn powf(&self, exp: &Self) -> Self {
205 if exp.eq_zero() || *self == Self::ONE {
208 return Self::ONE;
209 }
210 if *exp == Self::ONE {
212 return *self;
213 }
214 if *exp == Self::NEG_ONE {
216 return self.recip();
217 }
218 if self.is_special() || exp.is_special() {
221 if self.is_nan() || exp.is_nan() {
224 return Self::NAN;
225 }
226 if self.eq_zero() {
229 return [Self::ZERO, Self::INFINITY][exp.sign() as usize];
230 }
231 if self.is_infinite() {
238 return match (self.sign(), exp.sign()) {
239 (0, 0) => Self::INFINITY,
240 (0, 1) => Self::ZERO,
241 (1, 0) => match exp.parity() {
242 Some(Parity::Odd) => Self::NEG_INFINITY,
243 _ => Self::INFINITY,
244 },
245 (1, 1) => match exp.parity() {
246 Some(Parity::Odd) => Self::NEG_ZERO,
247 _ => Self::ZERO,
248 },
249 _ => unreachable!(),
250 };
251 }
252 }
253 powf(self, exp)
255 }
256}
257
258#[cfg(test)]
259mod powi_tests {
260 use super::*;
261 use crate::{EMAX, FRACTION_BITS, SIGNIFICAND_BITS};
262
263 #[test]
264 #[allow(clippy::cognitive_complexity)]
265 fn test_specials() {
266 for n in [1, 786, i32::MAX] {
267 assert_eq!(f256::INFINITY.powi(n), f256::INFINITY);
268 assert_eq!(f256::ZERO.powi(n), f256::ZERO);
269 assert!(f256::NAN.powi(n).is_nan());
270 }
271 for n in [i32::MIN, -328, -1] {
272 assert_eq!(f256::INFINITY.powi(n), f256::ZERO);
273 assert_eq!(f256::ZERO.powi(n), f256::INFINITY);
274 assert!(f256::NAN.powi(n).is_nan());
275 }
276 for n in [1, 783, i32::MAX] {
277 assert_eq!(f256::NEG_INFINITY.powi(n), f256::NEG_INFINITY);
278 }
279 for n in [4, 780, i32::MAX - 1] {
280 assert_eq!(f256::NEG_INFINITY.powi(n), f256::INFINITY);
281 }
282 for n in [i32::MIN, -328, -8] {
283 let z = f256::NEG_INFINITY.powi(n);
284 assert!(z.eq_zero());
285 assert!(z.is_sign_positive());
286 }
287 for n in [i32::MIN + 1, -321, -5] {
288 let z = f256::NEG_INFINITY.powi(n);
289 assert!(z.eq_zero());
290 assert!(z.is_sign_negative());
291 }
292 }
293
294 #[test]
295 fn test_n_eq_0() {
296 for f in [
297 f256::NAN,
298 f256::NEG_INFINITY,
299 f256::MIN,
300 -f256::TEN,
301 f256::NEG_ONE,
302 f256::NEG_ZERO,
303 f256::ZERO,
304 f256::MIN_GT_ZERO,
305 f256::MIN_POSITIVE,
306 f256::EPSILON,
307 f256::ONE,
308 f256::TWO,
309 f256::MAX,
310 f256::INFINITY,
311 ] {
312 assert_eq!(f.powi(0), f256::ONE);
313 }
314 }
315
316 #[test]
317 #[allow(clippy::integer_division)]
318 fn test_overflow() {
319 let mut f = f256::MAX.sqrt();
320 f += f.ulp();
321 assert_eq!(f.powi(2), f256::INFINITY);
322 let n = -7;
323 let f = f256::from_sign_exp_signif(1, EMAX / n - 1, (0, 1));
324 assert_eq!(f.powi(n), f256::NEG_INFINITY);
325 let n = 1440;
326 let f = f256::from_sign_exp_signif(
327 1,
328 -54,
329 (
330 0x00001be93972f42ce76d0fe4549e8709,
331 0x822fb626bcccea99631b77b8b3c24781,
332 ),
333 );
334 assert_eq!(f.powi(n), f256::INFINITY);
335 }
336
337 #[test]
338 #[allow(clippy::integer_division)]
339 #[allow(clippy::cast_possible_wrap)]
340 fn test_underflow() {
341 let mut f =
342 f256::MAX.sqrt() * f256::from(2_u128.pow(FRACTION_BITS / 2));
343 f += f.ulp();
344 assert_eq!(f.powi(-2), f256::ZERO);
345 let n = 7;
346 let f = f256::from_sign_exp_signif(
347 1,
348 (EMIN - FRACTION_BITS as i32) / n - 1,
349 (0, 1),
350 );
351 assert_eq!(f.powi(n), f256::NEG_ZERO);
352 let mut f = f256::MIN_GT_ZERO.sqrt().div2();
353 assert_eq!(f.powi(2), f256::ZERO);
354 }
355
356 #[test]
357 fn test_subnormal_result() {
358 let f = f256::MIN_GT_ZERO.sqrt();
359 assert_eq!(f.powi(2), f256::MIN_GT_ZERO);
360 }
361
362 #[test]
363 #[allow(clippy::cast_sign_loss)]
364 fn test_int_base_with_n_gt_0() {
365 let m = 73_u64;
366 let f = f256::from(m);
367 let n = 5;
368 assert_eq!(f.powi(n), f256::from(m.pow(n as u32)));
369 let m = 17_u128;
370 let f = f256::from(m);
371 let n = 30;
372 assert_eq!(f.powi(n), f256::from(m.pow(n as u32)));
373 }
374
375 #[test]
376 fn test_int_base_with_n_lt_0() {
377 let m = 69_u64;
378 let f = f256::from(m);
379 let n = -4;
380 assert_eq!(f.powi(n), f256::from(m.pow(n.unsigned_abs())).recip());
381 let m = 7_u128;
382 let f = f256::from(m);
383 let n = -41;
384 assert_eq!(f.powi(n), f256::from(m.pow(n.unsigned_abs())).recip());
385 }
386
387 #[test]
388 fn test_base_near_1() {
389 let f = f256::ONE + f256::EPSILON;
390 let f2 = f.square();
391 assert_eq!(f.powi(2), f2);
392 assert_eq!(f.powi(9), f2.square().square() * f);
393 }
394
395 #[test]
396 fn test_base_near_minus_1() {
397 let f = f256::NEG_ONE + f256::EPSILON;
398 let f2 = f.square();
399 assert_eq!(f.powi(2), f2);
400 assert_eq!(f.powi(5), f2.square() * f);
401 }
402
403 #[test]
404 #[allow(clippy::cast_possible_wrap)]
405 fn test_max() {
406 let f = f256::TWO;
407 let p =
408 (f.powi(EMAX) - f.powi(EMAX - SIGNIFICAND_BITS as i32)).mul2();
409 assert_eq!(p, f256::MAX);
410 }
411
412 #[test]
413 fn test_near_max() {
414 let mut f = f256::TWO;
415 f -= f.ulp();
416 let p = f.powi(f256::MAX_EXP);
417 assert!(p.diff_within_n_bits(&f256::MAX, 19));
418 }
419}
420
421#[cfg(test)]
422mod powf_tests {
423 use super::*;
424 use crate::{EMAX, FIVE, FRACTION_BITS, ONE_HALF};
425
426 #[test]
427 #[allow(clippy::cognitive_complexity)]
428 fn test_specials() {
429 let g = f256::from(123.45_f64);
430 let h = f256::TWO.powi(236) - f256::ONE;
431 for b in [f256::MIN_GT_ZERO, f256::ONE, g, f256::MAX] {
432 assert_eq!(f256::INFINITY.powf(&b), f256::INFINITY);
434 assert_eq!(f256::ZERO.powf(&b), f256::ZERO);
436 assert!(f256::NAN.powf(&b).is_nan());
438 }
439 for b in [f256::MIN, -g, f256::NEG_ONE, -f256::MIN_POSITIVE] {
440 assert_eq!(f256::INFINITY.powf(&b), f256::ZERO);
442 assert_eq!(f256::ZERO.powf(&b), f256::INFINITY);
444 assert!(f256::NAN.powf(&b).is_nan());
446 }
447 for b in [f256::ONE, FIVE, h] {
448 assert_eq!(f256::NEG_INFINITY.powf(&b), f256::NEG_INFINITY);
450 }
451 for b in [f256::TWO, g, f256::MAX] {
452 assert_eq!(f256::NEG_INFINITY.powf(&b), f256::INFINITY);
454 }
455 for b in [f256::MIN, -g, -f256::TEN] {
456 let z = f256::NEG_INFINITY.powf(&b);
458 assert!(z.eq_zero());
459 assert!(z.is_sign_positive());
460 }
461 for b in [-h, -FIVE] {
462 let z = f256::NEG_INFINITY.powf(&b);
464 assert!(z.eq_zero());
465 assert!(z.is_sign_negative());
466 }
467 for a in [
468 f256::NAN,
469 f256::NEG_INFINITY,
470 f256::MIN,
471 -f256::TEN,
472 f256::NEG_ONE,
473 f256::NEG_ZERO,
474 f256::ZERO,
475 f256::MIN_GT_ZERO,
476 f256::MIN_POSITIVE,
477 f256::EPSILON,
478 f256::TWO,
479 f256::MAX,
480 f256::INFINITY,
481 ] {
482 assert!(a.powf(&f256::NAN).is_nan());
484 }
485 for b in [
486 f256::NAN,
487 f256::NEG_INFINITY,
488 f256::MIN,
489 -f256::TEN,
490 f256::NEG_ONE,
491 f256::MIN_GT_ZERO,
492 f256::MIN_POSITIVE,
493 f256::EPSILON,
494 f256::ONE,
495 f256::TWO,
496 f256::MAX,
497 f256::INFINITY,
498 ] {
499 assert!(f256::NAN.powf(&b).is_nan());
501 }
502 for x in [f256::MIN, -g, f256::NEG_ONE, -f256::MIN_POSITIVE] {
503 assert!(x.powf(&g).is_nan());
505 }
506 }
507
508 #[test]
509 fn test_a_pow_0() {
510 for a in [
511 f256::NAN,
512 f256::NEG_INFINITY,
513 f256::MIN,
514 -f256::TEN,
515 f256::NEG_ONE,
516 f256::NEG_ZERO,
517 f256::ZERO,
518 f256::MIN_GT_ZERO,
519 f256::MIN_POSITIVE,
520 f256::EPSILON,
521 f256::ONE,
522 f256::TWO,
523 f256::MAX,
524 f256::INFINITY,
525 ] {
526 assert_eq!(a.powf(&f256::ZERO), f256::ONE);
528 assert_eq!(a.powf(&f256::NEG_ZERO), f256::ONE);
529 }
530 }
531
532 #[test]
533 fn test_a_pow_1() {
534 for a in [
536 f256::NEG_INFINITY,
537 f256::MIN,
538 -f256::TEN,
539 f256::NEG_ONE,
540 f256::NEG_ZERO,
541 f256::ZERO,
542 f256::MIN_GT_ZERO,
543 f256::MIN_POSITIVE,
544 f256::EPSILON,
545 f256::ONE,
546 f256::TWO,
547 f256::MAX,
548 f256::INFINITY,
549 ] {
550 assert_eq!(a.powf(&f256::ONE), a);
551 }
552 assert!(f256::NAN.powf(&f256::ONE).is_nan());
553 }
554
555 #[test]
556 fn test_1_pow_b() {
557 for b in [
558 f256::NAN,
559 f256::NEG_INFINITY,
560 f256::MIN,
561 -f256::TEN,
562 f256::NEG_ONE,
563 f256::NEG_ZERO,
564 f256::ZERO,
565 f256::MIN_GT_ZERO,
566 f256::MIN_POSITIVE,
567 f256::EPSILON,
568 f256::ONE,
569 f256::TWO,
570 f256::MAX,
571 f256::INFINITY,
572 ] {
573 assert_eq!(f256::ONE.powf(&b), f256::ONE);
575 }
576 }
577
578 #[test]
579 #[allow(clippy::integer_division)]
580 fn test_overflow() {
581 let mut x = f256::MAX.sqrt();
582 x += x.ulp();
583 assert_eq!(x.powf(&f256::TWO), f256::INFINITY);
584 let n = -7;
585 let mut y = f256::from(n);
586 y -= y.ulp();
587 let x = f256::from_sign_exp_signif(0, EMAX / n - 1, (0, 1));
588 assert_eq!(x.powf(&y), f256::INFINITY);
589 let y = f256::from(1439.907);
590 let x = f256::from_sign_exp_signif(
591 0,
592 -54,
593 (
594 0x00001be93972f42ce76d0fe4549e8709,
595 0x822fb626bcccea99631b77b8b3c24781,
596 ),
597 );
598 assert_eq!(x.powf(&y), f256::INFINITY);
599 }
600
601 #[test]
602 #[allow(clippy::integer_division)]
603 #[allow(clippy::cast_possible_wrap)]
604 fn test_underflow() {
605 let mut x =
606 f256::MAX.sqrt() * f256::from(2_u128.pow(FRACTION_BITS / 2));
607 x += x.ulp();
608 assert_eq!(x.powf(&-f256::TWO), f256::ZERO);
609 let n = 7;
610 let mut y = f256::from(n);
611 y += y.ulp();
612 let x = f256::from_sign_exp_signif(
613 0,
614 (EMIN - FRACTION_BITS as i32) / n - 1,
615 (0, 1),
616 );
617 assert_eq!(x.powf(&y), f256::ZERO);
618 let x = f256::MIN_GT_ZERO.sqrt().div2();
619 let mut y = f256::TWO;
620 y -= y.ulp();
621 assert_eq!(x.powf(&y), f256::ZERO);
622 }
623
624 #[test]
625 fn test_subnormal_result() {
626 let x = f256::MIN_GT_ZERO.sqrt();
627 let mut y = f256::TWO;
628 y -= y.ulp();
629 assert_eq!(x.powf(&y), f256::MIN_GT_ZERO);
630 let x = f256::EPSILON;
631 let y = f256::from(1111.1);
632 let z = x.powf(&y);
633 assert_eq!(
634 z,
635 f256::from_sign_exp_signif(
636 0,
637 -262377,
638 (
639 0x0000000000000000000000002a3968a7,
640 0x75598fceb5d84852b84918221a2a837f,
641 )
642 )
643 );
644 }
645
646 #[test]
647 fn test_base_near_1() {
648 let x = f256::ONE + f256::EPSILON;
649 let z = x.square();
650 let mut y = f256::TWO;
651 y -= z.ulp();
652 assert_eq!(x.powf(&y), z);
653 assert_eq!(x.powf(&f256::from(9)), z.square().square() * x);
654 }
655
656 #[test]
657 fn test_base_near_minus_1() {
658 let x = f256::NEG_ONE + f256::EPSILON;
659 let z = x.square();
660 let mut y = f256::TWO;
661 y -= z.ulp();
662 assert_eq!(x.powf(&y), z);
663 assert_eq!(x.powf(&f256::from(5)), z.square() * x);
664 }
665
666 #[test]
667 fn test_int_base_non_int_exp() {
668 let x = FIVE;
669 assert_eq!(x.powf(&f256::from(2.5_f64)), FIVE.square() * FIVE.sqrt());
670 }
671
672 #[test]
673 fn test_non_int_base_non_int_exp() {
674 let x = FIVE - f256::EPSILON.mul_pow2(7);
675 assert_eq!(
676 x.powf(&f256::from(3.25_f64)),
677 x.powi(3) * x.sqrt().sqrt()
678 );
679 }
680}