Much improvement of addition and subtraction
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
This commit is contained in:
parent
55d2d407e3
commit
84656e74c9
30
src/num.rs
30
src/num.rs
@ -90,6 +90,14 @@ pub trait Int:
|
|||||||
/// Is this number less than zero?
|
/// Is this number less than zero?
|
||||||
fn is_neg(self) -> bool;
|
fn is_neg(self) -> bool;
|
||||||
|
|
||||||
|
/// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic
|
||||||
|
/// overflow would occur. If an overflow would have occurred then the wrapped value is returned.
|
||||||
|
fn left_overflowing_sub(self, rhs: Self) -> (Self, bool);
|
||||||
|
|
||||||
|
/// Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic
|
||||||
|
/// overflow would occur. If an overflow would have occurred then the wrapped value is returned.
|
||||||
|
fn left_overflowing_mul(self, rhs: Self) -> (Self, bool);
|
||||||
|
|
||||||
/// Convert to an unsigned number
|
/// Convert to an unsigned number
|
||||||
///
|
///
|
||||||
/// A meaningless operation when implemented on an
|
/// A meaningless operation when implemented on an
|
||||||
@ -167,6 +175,28 @@ macro_rules! impl_int {
|
|||||||
// between the same bit size
|
// between the same bit size
|
||||||
<$un_type>::try_from(self).unwrap()
|
<$un_type>::try_from(self).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn left_overflowing_mul(self, rhs: Self) -> (Self, bool) {
|
||||||
|
let (res, overflow) = <$type>::overflowing_mul(self, rhs);
|
||||||
|
let res = if overflow {
|
||||||
|
<$type>::max_value() - res + 1
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
};
|
||||||
|
|
||||||
|
(res, overflow)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn left_overflowing_sub(self, rhs: Self) -> (Self, bool) {
|
||||||
|
let (res, overflow) = <$type>::overflowing_sub(self, rhs);
|
||||||
|
let res = if overflow {
|
||||||
|
<$type>::max_value() - res + 1
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
};
|
||||||
|
|
||||||
|
(res, overflow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! # Rational Numbers (fractions)
|
//! # Rational Numbers (fractions)
|
||||||
|
|
||||||
use crate::num::*;
|
use crate::num::*;
|
||||||
|
use crate::num::Sign::*;
|
||||||
use std::cmp::{Ord, Ordering, PartialOrd};
|
use std::cmp::{Ord, Ordering, PartialOrd};
|
||||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ macro_rules! frac {
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
enum FracOp {
|
enum FracOp {
|
||||||
|
Addition,
|
||||||
Subtraction,
|
Subtraction,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
@ -50,18 +52,18 @@ impl<T: Unsigned> Frac<T> {
|
|||||||
///
|
///
|
||||||
/// Generally, you will probably prefer to use the [frac!](../macro.frac.html) macro
|
/// Generally, you will probably prefer to use the [frac!](../macro.frac.html) macro
|
||||||
/// instead, as that is easier for mixed fractions and whole numbers
|
/// instead, as that is easier for mixed fractions and whole numbers
|
||||||
pub fn new<N: Int + Int<Un = T>>(n: N, d: N) -> Frac<T> {
|
pub fn new<N: Int<Un = T>>(n: N, d: N) -> Frac<T> {
|
||||||
Self::new_unreduced(n, d).reduce()
|
Self::new_unreduced(n, d).reduce()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new rational number from signed or unsigned arguments
|
/// Create a new rational number from signed or unsigned arguments
|
||||||
/// where the resulting fraction is not reduced
|
/// where the resulting fraction is not reduced
|
||||||
pub fn new_unreduced<N: Int + Int<Un = T>>(n: N, d: N) -> Frac<T> {
|
pub fn new_unreduced<N: Int<Un = T>>(n: N, d: N) -> Frac<T> {
|
||||||
if d.is_zero() {
|
if d.is_zero() {
|
||||||
panic!("Fraction can not have a zero denominator");
|
panic!("Fraction can not have a zero denominator");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sign = Sign::Positive;
|
let mut sign = Positive;
|
||||||
|
|
||||||
if n.is_neg() {
|
if n.is_neg() {
|
||||||
sign = !sign;
|
sign = !sign;
|
||||||
@ -71,6 +73,7 @@ impl<T: Unsigned> Frac<T> {
|
|||||||
sign = !sign;
|
sign = !sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the possibly signed arguments to unsigned values
|
||||||
let numer = n.to_unsigned();
|
let numer = n.to_unsigned();
|
||||||
let denom = d.to_unsigned();
|
let denom = d.to_unsigned();
|
||||||
|
|
||||||
@ -81,7 +84,7 @@ impl<T: Unsigned> Frac<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new rational from the raw parts
|
/// Create a new rational from all the raw parts
|
||||||
fn raw(n: T, d: T, s: Sign) -> Frac<T> {
|
fn raw(n: T, d: T, s: Sign) -> Frac<T> {
|
||||||
if d.is_zero() {
|
if d.is_zero() {
|
||||||
panic!("Fraction can not have a zero denominator");
|
panic!("Fraction can not have a zero denominator");
|
||||||
@ -96,14 +99,22 @@ impl<T: Unsigned> Frac<T> {
|
|||||||
|
|
||||||
/// Determine the output sign given the two input signs
|
/// Determine the output sign given the two input signs
|
||||||
fn get_sign(a: Self, b: Self, c: FracOp) -> Sign {
|
fn get_sign(a: Self, b: Self, c: FracOp) -> Sign {
|
||||||
if a.sign != b.sign {
|
if c == FracOp::Addition {
|
||||||
if c == FracOp::Subtraction && b.sign == Sign::Negative {
|
return if a.sign == Positive && b.sign == Positive {
|
||||||
Sign::Positive
|
Positive
|
||||||
} else {
|
} else {
|
||||||
Sign::Negative
|
Negative
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.sign != b.sign {
|
||||||
|
if c == FracOp::Subtraction && b.sign == Negative {
|
||||||
|
Positive
|
||||||
|
} else {
|
||||||
|
Negative
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Sign::Positive
|
Positive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +143,7 @@ where
|
|||||||
{
|
{
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
if self.sign != other.sign {
|
if self.sign != other.sign {
|
||||||
return if self.sign == Sign::Positive {
|
return if self.sign == Positive {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
} else {
|
} else {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
@ -147,7 +158,6 @@ where
|
|||||||
let mut b = other.reduce();
|
let mut b = other.reduce();
|
||||||
|
|
||||||
if a.denom == b.denom {
|
if a.denom == b.denom {
|
||||||
assert!(false, "{:#?}\n{:#?}", a, b);
|
|
||||||
return a.numer.cmp(&b.numer);
|
return a.numer.cmp(&b.numer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,12 +241,10 @@ where
|
|||||||
|
|
||||||
// If the sign of one input differs,
|
// If the sign of one input differs,
|
||||||
// subtraction is equivalent
|
// subtraction is equivalent
|
||||||
if self.sign != rhs.sign {
|
if a.sign == Negative && b.sign == Positive {
|
||||||
if a > b {
|
return b - -a;
|
||||||
return a - b;
|
} else if a.sign == Positive && b.sign == Negative {
|
||||||
} else if a < b {
|
return a - -b;
|
||||||
return b - a;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find a common denominator if needed
|
// Find a common denominator if needed
|
||||||
@ -245,14 +253,14 @@ where
|
|||||||
// worrying about reducing to the least common denominator
|
// worrying about reducing to the least common denominator
|
||||||
let numer = (a.numer * b.denom) + (b.numer * a.denom);
|
let numer = (a.numer * b.denom) + (b.numer * a.denom);
|
||||||
let denom = a.denom * b.denom;
|
let denom = a.denom * b.denom;
|
||||||
let sign = Self::get_sign(a, b, FracOp::Other);
|
let sign = Self::get_sign(a, b, FracOp::Addition);
|
||||||
|
|
||||||
return Self::raw(numer, denom, sign);
|
return Self::raw(numer, denom, sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
let numer = a.numer + b.numer;
|
let numer = a.numer + b.numer;
|
||||||
let denom = self.denom;
|
let denom = self.denom;
|
||||||
let sign = Self::get_sign(a, b, FracOp::Other);
|
let sign = Self::get_sign(a, b, FracOp::Addition);
|
||||||
|
|
||||||
Self::raw(numer, denom, sign)
|
Self::raw(numer, denom, sign)
|
||||||
}
|
}
|
||||||
@ -277,19 +285,31 @@ where
|
|||||||
let a = self;
|
let a = self;
|
||||||
let b = rhs;
|
let b = rhs;
|
||||||
|
|
||||||
// @TODO handle sign "overflow" conditions
|
if a.sign == Positive && b.sign == Negative {
|
||||||
|
return a + -b;
|
||||||
|
} else if a.sign == Negative && b.sign == Positive {
|
||||||
|
return b + -a;
|
||||||
|
}
|
||||||
|
|
||||||
if a.denom != b.denom {
|
if a.denom != b.denom {
|
||||||
let numer = (a.numer * b.denom) - (b.numer * a.denom);
|
let (numer, overflowed) = (a.numer * b.denom).left_overflowing_sub(b.numer * a.denom);
|
||||||
|
|
||||||
let denom = a.denom * b.denom;
|
let denom = a.denom * b.denom;
|
||||||
let sign = Self::get_sign(a, b, FracOp::Subtraction);
|
let mut sign = Self::get_sign(a, b, FracOp::Subtraction);
|
||||||
|
if overflowed {
|
||||||
|
sign = !sign
|
||||||
|
}
|
||||||
|
|
||||||
return Self::raw(numer, denom, sign);
|
return Self::raw(numer, denom, sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
let numer = a.numer - b.numer;
|
let (numer, overflowed) = a.numer.left_overflowing_sub(b.numer);
|
||||||
|
|
||||||
let denom = a.denom;
|
let denom = a.denom;
|
||||||
let sign = Self::get_sign(a, b, FracOp::Subtraction);
|
let mut sign = Self::get_sign(a, b, FracOp::Subtraction);
|
||||||
|
if overflowed {
|
||||||
|
sign = !sign
|
||||||
|
}
|
||||||
|
|
||||||
Self::raw(numer, denom, sign)
|
Self::raw(numer, denom, sign)
|
||||||
}
|
}
|
||||||
@ -308,7 +328,7 @@ impl<T: Unsigned> Neg for Frac<T> {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
let mut out = self.clone();
|
let mut out = self;
|
||||||
out.sign = !self.sign;
|
out.sign = !self.sign;
|
||||||
|
|
||||||
out
|
out
|
||||||
@ -331,16 +351,15 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_test() {
|
fn add_test() {
|
||||||
assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2));
|
assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2), "1/3 + 1/2");
|
||||||
assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3");
|
assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3");
|
||||||
// assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sub_test() {
|
fn sub_test() {
|
||||||
assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3));
|
assert_eq!(4u8.left_overflowing_sub(2).0, 2u8);
|
||||||
// assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3");
|
assert_eq!(0u8.left_overflowing_sub(2).0, 2u8);
|
||||||
// assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - 1/3");
|
assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3), "1/2 - 1/3");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -351,6 +370,14 @@ mod tests {
|
|||||||
assert_eq!(frac!(1 / 2), frac!(3 / 6));
|
assert_eq!(frac!(1 / 2), frac!(3 / 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn negative_fractions() {
|
||||||
|
assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3");
|
||||||
|
assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3");
|
||||||
|
// assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - +1/3");
|
||||||
|
assert_eq!(-frac!(1), -frac!(2 / 3) + -frac!(1/3), "-2/3 + -1/3");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_test() {
|
fn macro_test() {
|
||||||
let frac1 = frac!(1 / 3);
|
let frac1 = frac!(1 / 3);
|
||||||
|
Loading…
Reference in New Issue
Block a user