From d33b8303385881fe8dc48f50069718a127df17ca Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 13 Oct 2023 19:08:22 -0700 Subject: [PATCH] Ratio64: implement nearest fraction algorithm --- src/integer.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/integer.rs b/src/integer.rs index 6d4834f99..ec70b8a68 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -148,17 +148,39 @@ pub enum Ratio64TryFromFloatError{ HighlyNegativeExponent(i16), HighlyPositiveExponent(i16), } +const MAX_DENOMINATOR:u128=u64::MAX as u128; #[inline] fn ratio64_from_mes((m,e,s):(u64,i16,i8))->Result<Ratio64,Ratio64TryFromFloatError>{ if e< -127{ - //bye bye + //this can also just be zero Err(Ratio64TryFromFloatError::HighlyNegativeExponent(e)) }else if e< -63{ - //TODO - Err(Ratio64TryFromFloatError::HighlyNegativeExponent(e)) + //approximate input ratio within denominator limit + let mut target_num=m as u128; + let mut target_den=1u128<<-e; + + let mut num=1; + let mut den=0; + let mut prev_num=0; + let mut prev_den=1; + + while target_den!=0{ + let whole=target_num/target_den; + (target_num,target_den)=(target_den,target_num-whole*target_den); + let new_num=whole*num+prev_num; + let new_den=whole*den+prev_den; + if MAX_DENOMINATOR<new_den{ + break; + }else{ + (prev_num,prev_den)=(num,den); + (num,den)=(new_num,new_den); + } + } + + Ok(Ratio64::new(num as i64,den as u64).unwrap()) }else if e<0{ Ok(Ratio64::new((m as i64)*(s as i64),1<<-e).unwrap()) - }else if e<62-52{ + }else if (64-m.leading_zeros() as i16)+e<64{ Ok(Ratio64::new((m as i64)*(s as i64)*(1<<e),1).unwrap()) }else{ Err(Ratio64TryFromFloatError::HighlyPositiveExponent(e))