Number: fix frac.pow(frac) issue and better make_inexact() for sci
This commit fixes a bad procedure used to raise a fraction to the power of another fraction, and adds additional test cases for this. It also fixes a todo in the number package for implementing make_inexact on ScientificNotation numbers such that they can handle negative exponents. Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
parent
3174494001
commit
a48fc52fab
1 changed files with 58 additions and 48 deletions
|
|
@ -42,7 +42,7 @@ trait Numeric: Copy + Clone + Debug + FromStr + Into<String> {
|
|||
fn is_exact(&self) -> bool;
|
||||
fn make_inexact(&self) -> Float;
|
||||
fn make_exact(&self) -> Fraction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
|
@ -321,53 +321,38 @@ impl Pow<Number> for Number {
|
|||
type Output = Number;
|
||||
fn pow(self, rhs: Number) -> Self::Output {
|
||||
if self.is_exact() && rhs.is_exact() {
|
||||
let Fraction(lnum, lden) = self.make_exact();
|
||||
let Fraction(mut rnum, mut rden) = rhs.make_exact();
|
||||
let Fraction(mut lnum, mut lden) = self
|
||||
.make_exact()
|
||||
.simplify();
|
||||
let Fraction(rnum, rden) = rhs
|
||||
.make_exact()
|
||||
.simplify();
|
||||
|
||||
// normalize the negative to the top of the fraction
|
||||
if rden < 0 {
|
||||
rnum = 0 - rnum;
|
||||
rden = 0 - rden;
|
||||
// flip first frac if second one is negative
|
||||
if rnum < 0 {
|
||||
let tmp = lnum;
|
||||
lnum = lden;
|
||||
lden = tmp;
|
||||
}
|
||||
|
||||
// apply fractional exponent (denominator)
|
||||
let mut intermediate_numer = f64::powf(lnum as f64, 1.0 / rden as f64);
|
||||
let mut intermediate_denom = f64::powf(lden as f64, 1.0 / rden as f64);
|
||||
|
||||
// apply whole exponent (numerator)
|
||||
let mut intermediate_numer =
|
||||
pow::pow(lnum, rnum.abs() as usize) as f64;
|
||||
let mut intermediate_denom =
|
||||
pow::pow(lden, rnum.abs() as usize) as f64;
|
||||
intermediate_numer =
|
||||
f64::powf(intermediate_numer, rnum.abs() as f64);
|
||||
intermediate_denom =
|
||||
f64::powf(intermediate_denom, rnum.abs() as f64);
|
||||
|
||||
// handle negative exponent
|
||||
if rnum < 0 {
|
||||
intermediate_numer = 1.0 / intermediate_numer;
|
||||
intermediate_denom = 1.0 / intermediate_denom;
|
||||
}
|
||||
|
||||
// dont bother taking an nth root where n=1
|
||||
if rden == 1 {
|
||||
if intermediate_numer.fract() == 0.0 &&
|
||||
intermediate_denom.fract() == 0.0 {
|
||||
// return whatever the float decides :)
|
||||
if intermediate_numer.fract() == 0.0 && intermediate_denom.fract() == 0.0 {
|
||||
Number::Fra(Fraction(intermediate_numer as isize, intermediate_denom as isize)
|
||||
.simplify())
|
||||
} else {
|
||||
(intermediate_numer / intermediate_denom).into()
|
||||
|
||||
// we still have whole numbers everywhere
|
||||
} else {
|
||||
Number::Fra(Fraction(intermediate_numer as isize, intermediate_denom as isize))
|
||||
}
|
||||
|
||||
// gotta take an nth root (right hand denom > 1)
|
||||
} else {
|
||||
let num_res =
|
||||
f64::powf(intermediate_numer as f64, 1.0 / rden as f64);
|
||||
let den_res =
|
||||
f64::powf(intermediate_denom as f64, 1.0 / rden as f64);
|
||||
if num_res.fract() == 0.0 && den_res.fract() == 0.0 {
|
||||
Number::Fra(Fraction(num_res as isize, den_res as isize))
|
||||
|
||||
} else {
|
||||
(num_res / den_res).into()
|
||||
}
|
||||
}
|
||||
|
||||
// one or both are already inexact
|
||||
} else {
|
||||
let Float(l) = self.make_inexact();
|
||||
let Float(r) = rhs.make_inexact();
|
||||
|
|
@ -444,8 +429,21 @@ impl Numeric for SymbolicNumber {
|
|||
|
||||
impl Fraction {
|
||||
fn simplify(&self) -> Fraction {
|
||||
let g = gcd(self.0, self.1);
|
||||
Fraction(self.0 / g, self.1 / g)
|
||||
let mut n = self.0;
|
||||
let mut d = self.1;
|
||||
/* normalize negative to numerator
|
||||
* if numerator is also negative this becomes a positive frac
|
||||
*/
|
||||
if self.1 < 0 {
|
||||
d = 0 - self.1;
|
||||
n = 0 - self.0;
|
||||
}
|
||||
|
||||
/* divide both by their greatest common divisor
|
||||
* leading to simplest possible form
|
||||
*/
|
||||
let g = gcd(n, d);
|
||||
Fraction(n / g, d / g)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -572,8 +570,7 @@ impl Numeric for ScientificNotation {
|
|||
}
|
||||
|
||||
fn make_inexact(&self) -> Float {
|
||||
// TODO: This pow function needs to be replaced with one that can handle negative exponents
|
||||
Float(self.0 as f64 * pow::pow(10, self.1 as usize) as f64)
|
||||
Float(self.0 as f64 * f64::powi(10.0, self.1 as i32) as f64)
|
||||
}
|
||||
|
||||
fn make_exact(&self) -> Fraction {
|
||||
|
|
@ -750,7 +747,8 @@ mod tests {
|
|||
vec!["2/1", "2/1", "4/1"],
|
||||
vec!["2/1", "2/-1", "1/4"],
|
||||
vec!["2/1", "2/2", "2/1"],
|
||||
vec!["2/1", "2.0", "4/1"]
|
||||
vec!["2/1", "2.0", "4/1"],
|
||||
vec!["27/8", "2/-3", "4/9"]
|
||||
];
|
||||
|
||||
cases.iter().for_each(|case| {
|
||||
|
|
@ -779,4 +777,16 @@ mod tests {
|
|||
assert!(x < z);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_negative_exponent_case() {
|
||||
if let Float(0.1) = "1e-1"
|
||||
.parse::<Number>()
|
||||
.unwrap()
|
||||
.make_inexact() {
|
||||
return
|
||||
}
|
||||
|
||||
assert!(false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue