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

Commit 6374d6b

Browse files
authored
Merge pull request #128 from rrbutani/pow
Add some tests for pow
2 parents e714fae + ca14b0e commit 6374d6b

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed

libm/src/math/pow.rs

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

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

0 commit comments

Comments
 (0)