Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit 73b7434

Browse files
rrbutanialexcrichton
authored andcommitted
Add some tests for pow
These probably aren't comprehensive but they cover all the edge cases identified in the original musl source.
1 parent c2a1504 commit 73b7434

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed

libm/src/math/pow.rs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,192 @@ pub fn pow(x: f64, y: f64) -> f64 {
408408

409409
return s * z;
410410
}
411+
412+
/// Special cases:
413+
414+
/// 20. (anything) ** 1 is (anything)
415+
/// 21. (anything) ** -1 is 1/(anything)
416+
/// 22. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)
417+
/// 23. (-anything except 0 and inf) ** (non-integer) is NAN
418+
419+
#[cfg(test)]
420+
mod tests {
421+
// #[macro_use]
422+
extern crate std;
423+
424+
use self::std::f64::consts::{E, PI};
425+
use self::std::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY};
426+
use super::pow;
427+
428+
// const TESTCASES: &[f64] = &[1.0, 0.0, PI, -PI, E, -E, MIN, MAX, MIN_POSITIVE, NAN, INFINITY, NEG_INFINITY];
429+
430+
const POS_ZERO: &[f64] = &[0.0];
431+
const NEG_ZERO: &[f64] = &[-0.0];
432+
const POS_ONE: &[f64] = &[1.0];
433+
const NEG_ONE: &[f64] = &[-1.0];
434+
const POS_FLOATS: &[f64] = &[E, PI, MAX];
435+
const NEG_FLOATS: &[f64] = &[-E, -PI, MIN];
436+
const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON];
437+
const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON];
438+
const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0];
439+
const NEG_EVENS: &[f64] = &[-8.0, -2.0];
440+
const POS_ODDS: &[f64] = &[3.0, 7.0];
441+
const NEG_ODDS: &[f64] = &[-7.0, -3.0];
442+
const NANS: &[f64] = &[NAN];
443+
// const EDGES: &[f64] = &[MIN, MAX, MIN_POSITIVE, EPSILON];
444+
const POS_INF: &[f64] = &[INFINITY];
445+
const NEG_INF: &[f64] = &[NEG_INFINITY];
446+
447+
const ALL: &[&[f64]] = &[
448+
POS_ZERO, NEG_ZERO, NANS, NEG_SMALL_FLOATS, POS_SMALL_FLOATS, NEG_FLOATS, POS_FLOATS, NEG_EVENS, POS_EVENS, NEG_ODDS, POS_ODDS,
449+
NEG_INF, POS_INF, NEG_ONE, POS_ONE,
450+
];
451+
const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF];
452+
const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF];
453+
454+
fn pow_test(base: f64, exponent: f64, expected: f64) {
455+
let res = pow(base, exponent);
456+
assert!(if expected.is_nan() {res.is_nan()} else {pow(base, exponent) == expected},
457+
"{} ** {} was {} instead of {}", base, exponent, res, expected);
458+
}
459+
460+
fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) {
461+
sets.iter()
462+
.for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected)));
463+
}
464+
465+
fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) {
466+
sets.iter()
467+
.for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected)));
468+
}
469+
470+
fn test_sets(sets: &[&[f64]], computed: &Fn(f64) -> f64, expected: &Fn(f64) -> f64) {
471+
sets.iter()
472+
.for_each(|s| s.iter().for_each(|val| {
473+
let exp = expected(*val);
474+
let res = computed(*val);
475+
476+
assert!(if exp.is_nan() {res.is_nan()} else {exp == res},
477+
"test for {} was {} instead of {}", val, res, exp);
478+
}));
479+
}
480+
481+
/// 1. (anything) ** 0 is 1
482+
#[test]
483+
fn zero_as_exponent() {
484+
test_sets_as_base(ALL, 0.0, 1.0);
485+
test_sets_as_base(ALL, -0.0, 1.0);
486+
}
487+
488+
/// 2. 1 ** (anything) is 1
489+
#[test]
490+
fn one_as_base() {
491+
test_sets_as_exponent(1.0, ALL, 1.0);
492+
}
493+
494+
/// 3. (anything except 1) ** NAN is NAN
495+
/// 4. NAN ** (anything except 0) is NAN
496+
#[test]
497+
fn nan_inputs() {
498+
// NAN as the base:
499+
// (NAN ^ anything *but 0* should be NAN)
500+
test_sets_as_exponent(NAN, &ALL[2..], NAN);
501+
502+
// NAN as the exponent:
503+
// (anything *but 1* ^ NAN should be NAN)
504+
test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN);
505+
}
506+
507+
/// 16. +INF ** (+anything except 0,NAN) is +INF
508+
/// 17. +INF ** (-anything except 0,NAN) is +0
509+
/// 18. -INF ** (+odd integer) is -INF
510+
/// 19. -INF ** (anything) = -0 ** (-anything), (anything except odd integer)
511+
#[test]
512+
fn infinity_as_base() {
513+
// Positive Infinity as the base:
514+
// (+Infinity ^ positive anything but 0 and NAN should be +Infinity)
515+
test_sets_as_exponent(INFINITY, &POS[1..], INFINITY);
516+
517+
// (+Infinity ^ negative anything except 0 and NAN should be 0.0)
518+
test_sets_as_exponent(INFINITY, &NEG[1..], 0.0);
519+
520+
// Negative Infinity as the base:
521+
// (-Infinity ^ positive odd ints should be -Infinity)
522+
test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY);
523+
524+
// (-Infinity ^ anything but odd ints should be == -0 ^ (-anything))
525+
// We can lump in pos/neg odd ints here because they don't seem to
526+
// cause panics (div by zero) in release mode (I think).
527+
test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v));
528+
}
529+
530+
/// 5. +-(|x| > 1) ** +INF is +INF
531+
/// 6. +-(|x| > 1) ** -INF is +0
532+
/// 7. +-(|x| < 1) ** +INF is +0
533+
/// 8. +-(|x| < 1) ** -INF is +INF
534+
/// 9. -1 ** +-INF is 1
535+
#[test]
536+
fn infinity_as_exponent() {
537+
// Positive/Negative base greater than 1:
538+
// (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base)
539+
test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY);
540+
541+
// (pos/neg > 1 ^ -Infinity should be 0.0)
542+
test_sets_as_base(&ALL[5..(ALL.len() - 2)], NEG_INFINITY, 0.0);
543+
544+
// Positive/Negative base less than 1:
545+
let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS];
546+
547+
// (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base)
548+
test_sets_as_base(base_below_one, INFINITY, 0.0);
549+
550+
// (pos/neg < 1 ^ -Infinity should be Infinity)
551+
test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY);
552+
553+
// Positive/Negative 1 as the base:
554+
// (pos/neg 1 ^ Infinity should be 1)
555+
test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0);
556+
557+
// (pos/neg 1 ^ -Infinity should be 1)
558+
test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0);
559+
}
560+
561+
/// 10. +0 ** (+anything except 0, NAN) is +0
562+
/// 11. -0 ** (+anything except 0, NAN, odd integer) is +0
563+
/// 12. +0 ** (-anything except 0, NAN) is +INF, raise divbyzero
564+
/// 13. -0 ** (-anything except 0, NAN, odd integer) is +INF, raise divbyzero
565+
/// 14. -0 ** (+odd integer) is -0
566+
/// 15. -0 ** (-odd integer) is -INF, raise divbyzero
567+
#[test]
568+
fn zero_as_base() {
569+
// Positive Zero as the base:
570+
// (+0 ^ anything positive but 0 and NAN should be +0)
571+
test_sets_as_exponent(0.0, &POS[1..], 0.0);
572+
573+
// (+0 ^ anything negative but 0 and NAN should be Infinity)
574+
// (this should panic because we're dividing by zero but won't because release mode, I think)
575+
test_sets_as_exponent(0.0, &NEG[1..], INFINITY);
576+
577+
// Negative Zero as the base:
578+
// (-0 ^ anything positive but 0, NAN, and odd ints should be +0)
579+
test_sets_as_exponent(-0.0, &POS[3..], 0.0);
580+
581+
// (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity)
582+
// (should panic because of divide by zero)
583+
test_sets_as_exponent(-0.0, &NEG[3..], INFINITY);
584+
585+
// (-0 ^ positive odd ints should be -0)
586+
test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0);
587+
588+
// (-0 ^ negative odd ints should be -Infinity)
589+
// (should panic because of divide by zero)
590+
test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY);
591+
}
592+
593+
#[test]
594+
fn normal_cases() {
595+
assert_eq!(pow(2.0, 20.0), (1 << 20) as f64);
596+
assert_eq!(pow(-1.0, 9.0), -1.0);
597+
assert!(pow(-1.0, 2.2).is_nan());
598+
}
599+
}

0 commit comments

Comments
 (0)