Use sensible maths for from_rational (#13660)

* Use sensible maths for from_rational

* Fixes

* Fixes

* More fixes

* Remove debugging

* Add fuzzer tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Prevent panics

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* docs

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Clean up old code

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Test all rounding modes of from_rational

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Clean up code

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Revert "Prevent panics"

This reverts commit 7e88ac76138a1b590e68b68318505b69efe1e1f6.

* fix imports

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* cleanup

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fuzz test multiply_rational

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix import

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Return None in multiply_rational on zero div

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Gavin Wood
2023-03-24 14:48:37 +00:00
committed by GitHub
parent 40e1704e1c
commit 2c77f8f4ca
12 changed files with 372 additions and 227 deletions
@@ -284,6 +284,54 @@ impl PartialEq for Rational128 {
}
}
pub trait MultiplyRational: Sized {
fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self>;
}
macro_rules! impl_rrm {
($ulow:ty, $uhi:ty) => {
impl MultiplyRational for $ulow {
fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self> {
if d.is_zero() {
return None
}
let sn = (self as $uhi) * (n as $uhi);
let mut result = sn / (d as $uhi);
let remainder = (sn % (d as $uhi)) as $ulow;
if match r {
Rounding::Up => remainder > 0,
// cannot be `(d + 1) / 2` since `d` might be `max_value` and overflow.
Rounding::NearestPrefUp => remainder >= d / 2 + d % 2,
Rounding::NearestPrefDown => remainder > d / 2,
Rounding::Down => false,
} {
result = match result.checked_add(1) {
Some(v) => v,
None => return None,
};
}
if result > (<$ulow>::max_value() as $uhi) {
None
} else {
Some(result as $ulow)
}
}
}
};
}
impl_rrm!(u8, u16);
impl_rrm!(u16, u32);
impl_rrm!(u32, u64);
impl_rrm!(u64, u128);
impl MultiplyRational for u128 {
fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self> {
crate::helpers_128bit::multiply_by_rational_with_rounding(self, n, d, r)
}
}
#[cfg(test)]
mod tests {
use super::{helpers_128bit::*, *};