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
|
|
@ -321,53 +321,38 @@ impl Pow<Number> for Number {
|
||||||
type Output = Number;
|
type Output = Number;
|
||||||
fn pow(self, rhs: Number) -> Self::Output {
|
fn pow(self, rhs: Number) -> Self::Output {
|
||||||
if self.is_exact() && rhs.is_exact() {
|
if self.is_exact() && rhs.is_exact() {
|
||||||
let Fraction(lnum, lden) = self.make_exact();
|
let Fraction(mut lnum, mut lden) = self
|
||||||
let Fraction(mut rnum, mut rden) = rhs.make_exact();
|
.make_exact()
|
||||||
|
.simplify();
|
||||||
|
let Fraction(rnum, rden) = rhs
|
||||||
|
.make_exact()
|
||||||
|
.simplify();
|
||||||
|
|
||||||
// normalize the negative to the top of the fraction
|
// flip first frac if second one is negative
|
||||||
if rden < 0 {
|
if rnum < 0 {
|
||||||
rnum = 0 - rnum;
|
let tmp = lnum;
|
||||||
rden = 0 - rden;
|
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)
|
// apply whole exponent (numerator)
|
||||||
let mut intermediate_numer =
|
intermediate_numer =
|
||||||
pow::pow(lnum, rnum.abs() as usize) as f64;
|
f64::powf(intermediate_numer, rnum.abs() as f64);
|
||||||
let mut intermediate_denom =
|
intermediate_denom =
|
||||||
pow::pow(lden, rnum.abs() as usize) as f64;
|
f64::powf(intermediate_denom, rnum.abs() as f64);
|
||||||
|
|
||||||
// handle negative exponent
|
if intermediate_numer.fract() == 0.0 && intermediate_denom.fract() == 0.0 {
|
||||||
if rnum < 0 {
|
Number::Fra(Fraction(intermediate_numer as isize, intermediate_denom as isize)
|
||||||
intermediate_numer = 1.0 / intermediate_numer;
|
.simplify())
|
||||||
intermediate_denom = 1.0 / intermediate_denom;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// 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 :)
|
|
||||||
(intermediate_numer / intermediate_denom).into()
|
(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 {
|
} else {
|
||||||
let Float(l) = self.make_inexact();
|
let Float(l) = self.make_inexact();
|
||||||
let Float(r) = rhs.make_inexact();
|
let Float(r) = rhs.make_inexact();
|
||||||
|
|
@ -444,8 +429,21 @@ impl Numeric for SymbolicNumber {
|
||||||
|
|
||||||
impl Fraction {
|
impl Fraction {
|
||||||
fn simplify(&self) -> Fraction {
|
fn simplify(&self) -> Fraction {
|
||||||
let g = gcd(self.0, self.1);
|
let mut n = self.0;
|
||||||
Fraction(self.0 / g, self.1 / g)
|
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 {
|
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 * f64::powi(10.0, self.1 as i32) as f64)
|
||||||
Float(self.0 as f64 * pow::pow(10, self.1 as usize) as f64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_exact(&self) -> Fraction {
|
fn make_exact(&self) -> Fraction {
|
||||||
|
|
@ -750,7 +747,8 @@ mod tests {
|
||||||
vec!["2/1", "2/1", "4/1"],
|
vec!["2/1", "2/1", "4/1"],
|
||||||
vec!["2/1", "2/-1", "1/4"],
|
vec!["2/1", "2/-1", "1/4"],
|
||||||
vec!["2/1", "2/2", "2/1"],
|
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| {
|
cases.iter().for_each(|case| {
|
||||||
|
|
@ -779,4 +777,16 @@ mod tests {
|
||||||
assert!(x < z);
|
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