work
This commit is contained in:
@@ -23,35 +23,88 @@ fn f64_into_parts(f: f64) -> (u64, i16, i8) {
|
||||
|
||||
pub fn ratio_from_float(value:f64)->Result<Ratio64,RatioFromFloatError>{
|
||||
// Handle special values first
|
||||
match value.classify() {
|
||||
match value.classify(){
|
||||
core::num::FpCategory::Nan=>return Err(RatioFromFloatError::Nan),
|
||||
core::num::FpCategory::Zero=>return Ok(Ratio64::ZERO),
|
||||
core::num::FpCategory::Subnormal
|
||||
|core::num::FpCategory::Normal
|
||||
|core::num::FpCategory::Infinite=>{
|
||||
if value < i64::MIN as f64 {
|
||||
if value<i64::MIN as f64{
|
||||
return Err(RatioFromFloatError::Underflow);
|
||||
}
|
||||
if (i64::MAX as f64) < value {
|
||||
if (i64::MAX as f64)<value{
|
||||
return Err(RatioFromFloatError::Overflow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// value = sign * mantissa * 2 ^ exponent
|
||||
let (mantissa, exponent, sign) = f64_into_parts(value);
|
||||
let (mantissa,exponent,sign)=f64_into_parts(value);
|
||||
|
||||
if 0<=exponent{
|
||||
// we know value < i64::MIN, so it must just be an integer.
|
||||
return Ok(Ratio64::new((sign as i64*mantissa as i64)<<exponent,1).unwrap());
|
||||
}
|
||||
|
||||
if exponent< -62{
|
||||
if exponent< -62-52{
|
||||
// very small number is not representable
|
||||
return Ok(Ratio64::ZERO);
|
||||
}
|
||||
|
||||
// the idea: create exact float num/den ratio, and compute continued fraction if it doesn't fit
|
||||
// oh actually we can just straight up represent all floats exactly. ok.
|
||||
Ok(Ratio64::new(sign as i64*mantissa as i64, 1<<-exponent).unwrap())
|
||||
if -63<exponent{
|
||||
// exactly representable
|
||||
return Ok(Ratio64::new(sign as i64*mantissa as i64,1<<-exponent).unwrap());
|
||||
}
|
||||
|
||||
// the denominator is necessarily bigger than the numerator at this point.
|
||||
|
||||
// create exact float num/den ratio
|
||||
let mut in_num=mantissa as u128;
|
||||
let mut in_den=1u128<<-exponent;
|
||||
|
||||
let mut out_num=0i64;
|
||||
let mut out_den=1i64;
|
||||
|
||||
// a+b*c
|
||||
fn ma(a:i64,b:i64,c:i64)->Option<i64>{
|
||||
a.checked_add(b.checked_mul(c)?)
|
||||
}
|
||||
|
||||
// compute continued fraction
|
||||
loop{
|
||||
let whole=in_den/in_num;
|
||||
|
||||
if (i64::MAX as u128)<whole{
|
||||
// cannot continue fraction
|
||||
break;
|
||||
}
|
||||
|
||||
let Some(new_den)=ma(out_num,out_den,whole as i64)else{
|
||||
// too big for Ratio64
|
||||
break;
|
||||
};
|
||||
|
||||
(out_num,out_den)=(out_den,new_den);
|
||||
(in_num,in_den)=(in_den-in_num*whole,in_num);
|
||||
}
|
||||
|
||||
Ok(Ratio64::new(sign as i64*out_num,out_den as u64).unwrap())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn req(r0:Ratio64,r1:Ratio64){
|
||||
println!("r0={r0:?} r1={r1:?}");
|
||||
assert_eq!(r0.num(),r1.num(),"Nums not eq");
|
||||
assert_eq!(r0.den(),r1.den(),"Dens not eq");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test(){
|
||||
req(ratio_from_float(0.00000000001).unwrap(),Ratio64::new(1,10000000000).unwrap());
|
||||
req(ratio_from_float(0.5).unwrap(),Ratio64::new(1,2).unwrap());
|
||||
req(ratio_from_float(1.0).unwrap(),Ratio64::new(1,1).unwrap());
|
||||
req(ratio_from_float(1.1).unwrap(),Ratio64::new(2476979795053773,2251799813685248).unwrap());
|
||||
req(ratio_from_float(2.0).unwrap(),Ratio64::new(2,1).unwrap());
|
||||
req(ratio_from_float(core::f64::consts::PI).unwrap(),Ratio64::new(884279719003555,281474976710656).unwrap());
|
||||
req(ratio_from_float(2222222222222.0).unwrap(),Ratio64::new(2222222222222,1).unwrap());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user