ckb
ckb copied to clipboard
A better implementation for `RationalU256`.
Issue
Panicked at 'U256: attempt to multiply with overflow' for some values.
https://github.com/nervosnetwork/ckb/blob/649389d90a142cf39cdbc2019b8fbde188383ca5/util/rational/src/lib.rs#L128-L140
Reproduce
Multiply two RationalU256 numbers, when both two numerators (or denominators) are big but their greatest common divisor is small.
For example:
use ckb_types::{core::RationalU256, u256, U256};
fn main() {
let x = u256!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
let x_rational = RationalU256::from_u256(x.clone());
let numerator = 33u32;
let denominator = 100u32;
let y_rational = RationalU256::new(U256::from(numerator), U256::from(denominator));
let z_rational = x_rational * y_rational; // panicked in this line!
let z = z_rational.into_u256();
println!("{:#x} = {:#x} * {}/{}", z, x, numerator, denominator);
}
Ref: nervosnetwork/ckb-light-client#44
use ckb_types::{core::RationalU256, u256, U256}; fn main() { let x = u256!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); let x_rational = RationalU256::from_u256(x.clone()); let numerator = 33u32; let denominator = 100u32; let y_rational = RationalU256::new(U256::from(numerator), U256::from(denominator)); let z_rational = x_rational * y_rational; // panicked in this line! let z = z_rational.into_u256(); println!("{:#x} = {:#x} * {}/{}", z, x, numerator, denominator); }
I think It's expected that the very large x_rational multiply with y_rational cause overflow and panic.
If the multiplication of two values cause overflow, then it warns developer to use larger type like U512 or U1024.
If we don't do that, the result may lose precision, like:
impl Mul<&RationalU256> for &RationalU256 {
type Output = RationalU256;
fn mul(self, rhs: &RationalU256) -> RationalU256 {
let gcd_ad = self.numer.gcd(&rhs.denom);
let gcd_bc = self.denom.gcd(&rhs.numer);
let (numer1, _): (U512, bool) = (&self.numer / &gcd_ad).convert_into();
let (denom1, _): (U512, bool) = (&rhs.numer / &gcd_bc).convert_into();
let number: U512 = numer1 * denom1;
let (numer2, _): (U512, bool) = (&self.denom / &gcd_bc).convert_into();
let (denom2, _): (U512, bool) = (&rhs.denom / &gcd_ad).convert_into();
let denom: U512 = numer2 * denom2;
// put U512/U512 to U256/U256 may lose precision, this is not expected
let result :RationalU256 = RationalU512::new_raw(number, denom).reduce_to_U256();
result
}
}