Improve std::num::pow implementation
The implementation has been made more succinct and no longer requires Clone. The coverage of the associated unit test has also been increased to check more combinations of bases, exponents, and expected results.
This commit is contained in:
@@ -304,48 +304,29 @@ pub trait Real: Signed
|
|||||||
fn to_radians(&self) -> Self;
|
fn to_radians(&self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raises a value to the power of exp, using
|
/// Raises a value to the power of exp, using exponentiation by squaring.
|
||||||
/// exponentiation by squaring.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::num;
|
/// use std::num;
|
||||||
///
|
///
|
||||||
/// let sixteen = num::pow(2, 4u);
|
/// assert_eq!(num::pow(2, 4), 16);
|
||||||
/// assert_eq!(sixteen, 16);
|
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pow<T: Clone+One+Mul<T, T>>(num: T, exp: uint) -> T {
|
pub fn pow<T: One + Mul<T, T>>(mut base: T, mut exp: uint) -> T {
|
||||||
let one: uint = One::one();
|
if exp == 1 { base }
|
||||||
let num_one: T = One::one();
|
else {
|
||||||
|
let mut acc = one::<T>();
|
||||||
if exp.is_zero() { return num_one; }
|
while exp > 0 {
|
||||||
if exp == one { return num.clone(); }
|
if (exp & 1) == 1 {
|
||||||
|
acc = acc * base;
|
||||||
let mut i: uint = exp;
|
|
||||||
let mut v: T;
|
|
||||||
let mut r: T = num_one;
|
|
||||||
|
|
||||||
// This if is to avoid cloning self.
|
|
||||||
if (i & one) == one {
|
|
||||||
r = r * num;
|
|
||||||
i = i - one;
|
|
||||||
}
|
}
|
||||||
|
base = base * base;
|
||||||
i = i >> one;
|
exp = exp >> 1;
|
||||||
v = num * num;
|
|
||||||
|
|
||||||
while !i.is_zero() {
|
|
||||||
if (i & one) == one {
|
|
||||||
r = r * v;
|
|
||||||
i = i - one;
|
|
||||||
}
|
}
|
||||||
i = i >> one;
|
acc
|
||||||
v = v * v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raise a number to a power.
|
/// Raise a number to a power.
|
||||||
@@ -1670,17 +1651,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pow() {
|
fn test_pow() {
|
||||||
fn assert_pow<T: Eq+Clone+One+Mul<T, T>>(num: T, exp: uint) -> () {
|
fn naive_pow<T: One + Mul<T, T>>(base: T, exp: uint) -> T {
|
||||||
assert_eq!(num::pow(num.clone(), exp),
|
range(0, exp).fold(one::<T>(), |acc, _| acc * base)
|
||||||
range(1u, exp).fold(num.clone(), |acc, _| acc * num));
|
|
||||||
}
|
}
|
||||||
|
macro_rules! assert_pow(
|
||||||
assert_eq!(num::pow(3, 0), 1);
|
(($num:expr, $exp:expr) => $expected:expr) => {{
|
||||||
assert_eq!(num::pow(5, 1), 5);
|
let result = pow($num, $exp);
|
||||||
assert_pow(-4, 2);
|
assert_eq!(result, $expected);
|
||||||
assert_pow(8, 3);
|
assert_eq!(result, naive_pow($num, $exp));
|
||||||
assert_pow(8, 5);
|
}}
|
||||||
assert_pow(2u64, 50);
|
)
|
||||||
|
assert_pow!((3, 0 ) => 1);
|
||||||
|
assert_pow!((5, 1 ) => 5);
|
||||||
|
assert_pow!((-4, 2 ) => 16);
|
||||||
|
assert_pow!((0.5, 5 ) => 0.03125);
|
||||||
|
assert_pow!((8, 3 ) => 512);
|
||||||
|
assert_pow!((8.0, 5 ) => 32768.0);
|
||||||
|
assert_pow!((8.5, 5 ) => 44370.53125);
|
||||||
|
assert_pow!((2u64, 50) => 1125899906842624);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user