C# - Algorithm For Simplifying Decimal To Fractions - Stack Overflow
C# - Algorithm For Simplifying Decimal To Fractions - Stack Overflow
31
Itriedwritinganalgorithmtosimplifyadecimaltoafractionandrealizeditwasn'ttoosimple.
SurprisinglyIlookedonlineandallthecodesIfoundwhereeithertoolong,orwouldn'tworkinsome
cases.Whatwasevenmoreannoyingwasthattheydidn'tworkforrecurringdecimals.Iwas
wonderinghoweverwhethertherewouldbeamathematician/programmerherewhounderstandsall
theinvolvedprocessesinsimplifyingadecimaltoafraction.Anyone?
c# .net algorithm math
10
share improvethisquestion
askedFeb26'11at2:55
ChibuezeOpata
5,091
23
48
16Answers
31
active
oldest
votes
ThealgorithmthattheotherpeoplehavegivenyougetstheanswerbycalculatingtheContinued
Fractionofthenumber.Thisgivesafractionalsequencewhichisguaranteedtoconvergevery,very
rapidly.Howeveritisnotguaranteedtogiveyouthesmallestfractionthatiswithinadistanceepsilon
ofarealnumber.TofindthatyouhavetowalktheSternBrocottree.
Todothatyousubtractoffthefloortogetthenumberintherange[0,1),thenyourlowerestimateis0,
andyourupperestimateis1.Nowdoabinarysearchuntilyouarecloseenough.Ateachiterationif
yourlowerisa/bandyourupperisc/dyourmiddleis(a+c)/(b+d).Testyourmiddleagainstx,and
eithermakethemiddletheupper,thelower,orreturnyourfinalanswer.
Hereissomeverynonidiomatic(andhence,hopefully,readableevenifyoudon'tknowthelanguage)
Pythonthatimplementsthisalgorithm.
deffloat_to_fraction(x,error=0.000001):
n=int(math.floor(x))
x=n
ifx<error:
return(n,1)
elif1error<x:
return(n+1,1)
#Thelowerfractionis0/1
lower_n=0
lower_d=1
#Theupperfractionis1/1
upper_n=1
upper_d=1
whileTrue:
#Themiddlefractionis(lower_n+upper_n)/(lower_d+upper_d)
middle_n=lower_n+upper_n
middle_d=lower_d+upper_d
#Ifx+error<middle
ifmiddle_d*(x+error)<middle_n:
#middleisournewupper
upper_n=middle_n
upper_d=middle_d
#ElseIfmiddle<xerror
elifmiddle_n<(xerror)*middle_d:
#middleisournewlower
lower_n=middle_n
lower_d=middle_d
#Elsemiddleisourbestfraction
else:
return(n*middle_d+middle_n,middle_d)
share improvethisanswer
editedFeb26'11at18:13
answeredFeb26'11at17:36
btilly
20.1k
29
46
+1thisisagreatsolutionforfindingsmooth,humanfriendlyfractions.TimMedoraNov19'12at6:59
3 TranslatedthistoC#andaddedtestresultsforthisalgorithmseemyanswerKayZedOct2'15at9:01
Icameupwithanother,apparentlyfaster,solutionPinkFloydJun1at16:03
addacomment
Iknowyousaidyousearchedonline,butifyoumissedthefollowingpaperitmightbeofsomehelp.It
includesacodeexampleinPascal.
13
AlgorithmToConvertADecimalToAFraction*
Alternatively,aspartofit'sstandardlibrary,Rubyhascodethatdealswithrationalnumbers.Itcan
convertfromfloatstorationalsandviceversa.Ibelieveyoucanlookthroughthecodeaswell.The
documentationisfoundhere.Iknowyou'renotusingRuby,butitmighthelptolookatthealgorithms.
Additionally,youcancallRubycodefromC#(orevenwriteRubycodeinsideaC#codefile)ifyou
useIronRuby,whichrunsontopofthe.netframework.
*Updatedtoanewlinkusinghttp://www.docstoc.com/asitappearstheoriginalURLisnobroken
(http://homepage.smc.edu/kennedy_john/DEC2FRAC.pdf)
share improvethisanswer
editedFeb26'13at20:59
answeredFeb26'11at3:04
Matt
9,053
31
55
Thisisreallyagreatarticle,andIthinkthat'swhatmostareusing,butithappensthepersonwhoI
downloadedhiscode(translatedthecodetoc#)didn'ttoitwell.I'lltestitnow,:) ChibuezeOpata Feb26
'11at3:31
Thelinkleadstoanerrorpage.DanielFeb26'13at10:28
@Daniel:Tryitnow.MattFeb26'13at20:59
@MattItworks,thanks!DanielFeb26'13at22:13
addacomment
IfoundthesamepaperthatMattreferenced,andItookasecondandimplementeditinPython.
Maybeseeingthesameideaincodewillmakeitclearer.Granted,yourequestedananswerinC#
andI'mgivingittoyouinPython,butit'safairlytrivialprogram,andI'msureitwouldbeeasyto
translate.Theparametersare num (thedecimalnumberyou'dliketoconverttoarational)
and epsilon (themaximumalloweddifferencebetween num andthecalculatedrational).Some
quicktestrunsfindthatitusuallyonlytakestwoorthreeiterationstoconvergewhen epsilon is
around1e4.
defdec2frac(num,epsilon,max_iter=20):
d=[0,1]+([0]*max_iter)
z=num
n=1
t=1
whilenumandt<max_iterandabs(n/d[t]num)>epsilon:
t+=1
z=1/(zint(z))
d[t]=d[t1]*int(z)+d[t2]
#int(x+0.5)isequivalenttoroundingx.
n=int(num*d[t]+0.5)
returnn,d[t]
Edit:Ijustnoticedyournoteaboutwantingthemtoworkwithrecurringdecimals.Idon'tknowany
languagesthathavesyntaxtosupportrecurringdecimals,soI'mnotsurehowonewouldgoabout
handlingthem,butrunning0.6666666and0.166666throughthismethodreturnthecorrectresults(2/3
and1/6,respectively).
AnotherEdit(Ididn'tthinkthiswouldbesointeresting!):Ifyouwanttoknowmoreaboutthetheory
behindthisalgorithm,WikipediahasanexcellentpageontheEuclidianalgorithm
share improvethisanswer
editedFeb26'11at3:29
answeredFeb26'11at3:24
HaldeanBrown
5,122
20
40
2 Youdon'tneedanarray,btwIpostedananswersomewhereonSOonceexpressingthesamealgorithmas
aPythongenerator(whichavoidstheneedforepsilonandmax_iterinthecorelogicaswell).
DariusBaconFeb26'11at20:31
Ah,here:stackoverflow.com/questions/445113/DariusBaconFeb26'11at20:48
Yeah,initiallyIjustdidwithwithd0andd1,butwaslessreadablesoIwentwiththelistinstead.Also,
max_iterandepsilonjustgetmovedelsewhereifyoutakethemout,andIthinkitwouldbemoreconvenient
foranAPIusertodothewholethinginasinglefunctioncall,ratherthanrequirethecallertodotheiteration
themselves.HaldeanBrownMar1'11at4:50
addacomment
Iimplementedbtilly'sanswerinC#and...
addedsupportfornegativenumbers
changed error tobethemax.relativeerror,notthemax.absoluteerror
Double.NaN and Double.Infinity arenotsupportedyoumightwanttohandlethosetoo
(example).
publicstaticFractionRealToFraction(doublevalue,doubleerror)
{
if(error<=0.0||error>=1.0)
{
thrownewArgumentOutOfRangeException("error","Mustbebetween0and1(exclusive)."
}
intsign=Math.Sign(value);
if(sign==1)
{
value=Math.Abs(value);
}
if(sign!=0)
{
//erroristhemaximumrelativeerror;converttoabsolute
error*=value;
}
intn=(int)Math.Floor(value);
value=n;
if(value<error)
{
returnnewFraction(sign*n,1);
}
if(1error<value)
{
returnnewFraction(sign*(n+1),1);
}
//Thelowerfractionis0/1
intlower_n=0;
intlower_d=1;
//error=0.001
0>0/1(zero)0.00(0iterations)
0.1>1/100.00%(9iterations)
0.2>1/50.00%(4iterations)
0.3>3/100.00%(5iterations)
0.4>2/50.00%(3iterations)
0.5>1/20.00%(1iterations)
0.6>3/50.00%(3iterations)
0.7>7/100.00%(5iterations)
0.8>4/50.00%(4iterations)
0.9>9/100.00%(9iterations)
0.01>1/1000.00%(99iterations)
0.001>1/10000.00%(999iterations)
0.0001>1/99910.09%(9990iterations)
0.00001>1/999010.10%(99900iterations)
0.33333333333>1/30.00%(2iterations)
0.3>3/100.00%(5iterations)
0.33>30/910.10%(32iterations)
0.333>167/5020.10%(169iterations)
0.7777>7/90.01%(5iterations)
0.101>10/990.01%(18iterations)
0.10001>1/100.01%(9iterations)
0.100000001>1/100.00%(9iterations)
0.001001>1/9990.00%(998iterations)
0.0010000001>1/10000.00%(999iterations)
0.11>10/910.10%(18iterations)
0.1111>1/90.01%(8iterations)
0.111111111111>1/90.00%(8iterations)
1>1/10.00%(0iterations)
1>1/10.00%(0iterations)
0.5>1/20.00%(1iterations)
3.14>22/70.09%(6iterations)
3.1416>22/70.04%(6iterations)
3.14159265358979>22/70.04%(6iterations)
0.14>7/500.00%(13iterations)
0.1416>15/1060.06%(21iterations)
0.141592653589793>15/1060.06%(21iterations)
editedJan6at8:15
answeredOct2'15at8:57
KayZed
486
20
Thereasonwhythenumberofiterationsgetslargeisbecausetogetto1/100youaretrying1/2,1/3,1/4,...
Insteadonceyoustartgoingdownonesideofthetreeyoucandoabinarysearchthroughnotchangingthat
side.Thiswillgiveyou1/2,1/4,1/8,1/16,1/32,1/64,1/128,1/96,1/112,1/104,1/100.MUCHbetter.Ididn't
implementthattrickinmyanswerbecauseIwastryingtoexplain,notoptimize.btillyJan6at18:48
@btillyIknowyouansweredthisquestionalongtimeagobutIwouldliketoknowIyoucanpointwhereI
canfindinformationofthisoptimization.Idon'tunderstandwhatyoumeanandIcan'tfindinformation.Maybe
ifyoucouldupdateyouranswerwithalinkoramoredetaileddescriptionPinkFloydJun1at13:50
addacomment
Here'saC#versionofWillBrown'spythonexample.I'vealsochangedittohandleseparatewhole
numbers(e.g."21/8"insteadof"17/8").
publicstaticstringDoubleToFraction(doublenum,doubleepsilon=0.0001,intmaxIterations
{
double[]d=newdouble[maxIterations+2];
d[1]=1;
doublez=num;
doublen=1;
intt=1;
intwholeNumberPart=(int)num;
doubledecimalNumberPart=numConvert.ToDouble(wholeNumberPart);
while(t<maxIterations&&Math.Abs(n/d[t]num)>epsilon)
{
t++;
z=1/(z(int)z);
d[t]=d[t1]*(int)z+d[t2];
n=(int)(decimalNumberPart*d[t]+0.5);
}
returnstring.Format((wholeNumberPart>0?wholeNumberPart.ToString()+"":"")+
n.ToString(),
d[t].ToString()
);
}
share improvethisanswer
answeredJun10'13at6:46
JeremyHerrman
335
14
addacomment
Youcan'trepresentarecurringdecimalin.netsoI'llignorethatpartofyourquestion.
Youcanonlyrepresentafiniteandrelativelysmallnumberofdigits.
There'sanextremelysimplealgorithm:
takedecimal x
countthenumberofdigitsafterthedecimalpointcallthis n
createafraction (10^n*x)/10^n
removecommonfactorsfromthenumeratoranddenominator.
soifyouhave0.44,youwouldcount2placesarethedecimalpointn=2,andthenwrite
(0.44*10^2)/10^2
= 44/100
factorising(removingcommonfactorof4)gives 11/25
share improvethisanswer
answeredFeb26'11at3:10
KirkBroadhurst
15.8k
48
101
nice,butyoucandetectifadecimalisrecurringin.netright?Ihavealreadytriedsomethinglikethisandthis
isnotwhatIwant.Also,doyouknowthebestwaytofindandremovethecommonfactors?
ChibuezeOpata Feb26'11at3:17
It'sirrelevantwhetheryoucandetectifadecimalisrecurring,becauseyoucannothaverecurringdecimals.
Itissimplynotpossiblefora decimal typetoberecurring.KirkBroadhurstFeb26'11at3:41
hmm.seemsIwillbeneedingmoremasstuts:owhatexactlyareyoutryingtotellme??
ChibuezeOpata Feb26'11at4:07
2 You'reusing.net,inwhichthedecimaltypecanhavelessthan30digits.Itcannothaveinfinitedigits.Ithas
nowaytorepresent'recurring'patterns.Youcanhave0.333333333333333333butyoucannothave0.3*
(recurring)andtheyarenotthesamething.0.3*is1/3,buttheformeris33333333(etc)/1000000slightly
lessthan1/3.KirkBroadhurstFeb26'11at4:57
2 Themachinecanonlyknowwhatyoutellitsoifyouwanttodefinesomerulesto'round'clumsy20digit
fractiontoanicefractionyoucould:iftherearemorethan10digits,andthere'sa1or2digitfractionthatis
within0.1%orsomeothermarginthenrounditoff.Butit'suptoyoutodeterminethoserules.Thefact
remainsthat0.33333333333333333333isnotthesameas1/3.KirkBroadhurstFeb28'11at0:41
show5morecomments
IwroteaquickclassthatrunsfairlyquickandgivestheresultsIwouldexpect.Youcanchooseyour
Precisionaswell.ItismuchsimplerfromanycodeIseenandrunsquickaswell.
//WrittenByBrianDobony
publicstaticclassFraction
{
publicstaticstringConvertDecimal(DoubleNumberToConvert,intDenominatorPercision=
{
intWholeNumber=(int)NumberToConvert;
doubleDecimalValue=NumberToConvertWholeNumber;
doubledifference=1;
intnumerator=1;
intdenominator=1;
//findclosestvaluethatmatchespercision
//AutomaticallyfindsFractioninsimplifiedform
for(inty=2;y<DenominatorPercision+1;y++)
{
for(intx=1;x<y;x++)
{
doubletempdif=Math.Abs(DecimalValue(double)x/(double)y);
if(tempdif<difference)
{
numerator=x;
denominator=y;
difference=tempdif;
//ifexactmatchisfoundreturnit
if(difference==0)
{
returnFractionBuilder(WholeNumber,numerator,denominator);
}
}
}
}
returnFractionBuilder(WholeNumber,numerator,denominator);
}
privatestaticstringFractionBuilder(intWholeNumber,intNumerator,intDenominator)
{
share improvethisanswer
answeredMay21'15at21:41
BrianDobony
31
Itriedtheprogram,itisgoodfor'seamingly'repeatingdecimals,butitdidnotworkasIexpectedforsome
fractions,forexample:whenIusedthevalue:0.068376968,with32precision,theresultwas2/29
=.068965517,whichisgoodforonly4digitsbehindthedecimal.However,itisOKforme.NoChanceApr
11at19:36
addacomment
ThisalgorithmbyDavidEppstein,UCIrvine,basedonthetheoryofcontinuedfractionsandoriginally
inC,wastranslatedtoC#byme.Thefractionsitgeneratessatisfytheerrormarginbutmostlydonot
lookasgoodasthesolutioninmyotheranswer.E.g. 0.5 becomes 999/1999 while 1/2 wouldbe
preferredwhendisplayedtoauser(ifyouneedthat,seemyotheranswer).
Itisdefinitelyfastthough.
Thereisanoverloadtospecifytheerrormarginasadouble(relativetothevalue,nottheabsolute
error).Forthe Fraction type,seemyotheranswer.
Bytheway,ifyourfractionscangetlarge,changethe int sto long .
publicstaticFractionRealToFraction(doublevalue,intmaxDenominator)
{
//http://www.ics.uci.edu/~eppstein/numth/frap.c
//Findrationalapproximationtogivenrealnumber
//DavidEppstein/UCIrvine/8Aug1993
//WithcorrectionsfromArnoFormella,May2008
if(value==0.0)
{
returnnewFraction(0,1);
}
intsign=Math.Sign(value);
if(sign==1)
{
value=Math.Abs(value);
}
int[,]m={{1,0},{0,1}};
intai=(int)value;
//Findtermsuntildenominatorgetstoobig
while(m[1,0]*ai+m[1,1]<=maxDenominator)
{
intt=m[0,0]*ai+m[0,1];
m[0,1]=m[0,0];
m[0,0]=t;
t=m[1,0]*ai+m[1,1];
m[1,1]=m[1,0];
m[1,0]=t;
value=1.0/(valueai);
//0x7FFFFFFF=Assumes32bitfloatingpointjustlikeintheCimplementation.
//ThischeckincludesDouble.IsInfinity().EventhoughC#doubleis64bits,
//thealgorithmsometimesfailswhentryingtoincreasethisvaluetoomuch.So
Testresults:
//error=0.001
0>0/1(zero)0(0iterations)
0.1>999/99910.01%(2iterations)
0.2>999/49960.02%(2iterations)
0.3>998/33270.01%(4iterations)
0.4>999/24970.02%(3iterations)
0.5>999/19990.05%(2iterations)
0.6>1000/16670.02%(4iterations)
0.7>996/14230.01%(4iterations)
0.8>997/12460.02%(3iterations)
0.9>998/11090.01%(4iterations)
0.01>999/999010.00%(2iterations)
0.001>999/9990010.00%(2iterations)
0.0001>999/99900010.00%(2iterations)
0.00001>1000/999999990.00%(3iterations)
0.33333333333>1000/30010.03%(2iterations)
0.3>998/33270.01%(4iterations)
0.33>991/30030.00%(3iterations)
0.333>1000/30030.00%(3iterations)
0.7777>997/12820.00%(4iterations)
0.101>919/90990.00%(5iterations)
0.10001>1/100.01%(4iterations)
0.100000001>1000/99990.01%(3iterations)
0.001001>1/9990.00%(3iterations)
0.0010000001>1000/9999990.00%(3iterations)
0.11>1000/90910.00%(4iterations)
0.1111>1000/90010.00%(2iterations)
0.111111111111>1000/90010.01%(2iterations)
1>1001/10000.10%(1iterations)
1>1001/10000.10%(1iterations)
0.5>999/19990.05%(2iterations)
3.14>964/3070.00%(3iterations)
3.1416>732/2330.00%(3iterations)
3.14159265358979>688/2190.00%(4iterations)
0.14>995/71070.00%(3iterations)
0.1416>869/61370.00%(5iterations)
0.141592653589793>991/69990.00%(4iterations)
share improvethisanswer
editedJan6at7:54
answeredOct2'15at11:16
KayZed
486
20
addacomment
Arecurringdecimalcanberepresentedbytwofinitedecimals:theleftwardpartbeforetherepeat,and
therepeatingpart.E.g. 1.6181818...=1.6+0.1*(0.18...) .Thinkofthisas a+b*sum(c*
10**(d*k)forkinrange(1,infinity)) (inPythonnotationhere).Inmy
example, a=1.6 , b=0.1 , c=18 , d=2 (thenumberofdigitsin c ).Theinfinitesumcanbesimplified
( sum(r**kforrinrange(1,infinity))==r/(1r) ifIrecallrightly),yielding a+b*(c*
10**d)/(1c*10**d)) ,afiniteratio.Thatis,startwith a , b , c ,and d asrationalnumbers,
andyouendupwithanother.
(ThiselaboratesKirkBroadhurst'sanswer,whichisrightasfarasitgoes,butdoesn'tcoverrepeating
decimals.Idon'tpromiseImadenomistakesabove,thoughI'mconfidentthegeneralapproach
works.)
share improvethisanswer
editedFeb26'11at21:37
answeredFeb26'11at21:19
DariusBacon
11.3k
36
48
addacomment
IrecentlyhadtoperformthisverytaskofworkingwithaDecimalDataTypewhichisstoredinour
SQLServerdatabase.AtthePresentationLayerthisvaluewaseditedasafractionalvalueina
TextBox.ThecomplexityherewasworkingwiththeDecimalDataTypewhichholdssomeprettylarge
valuesincomparisontointorlong.Sotoreducetheopportunityfordataoverrun,Istuckwiththe
DecimalDataTypethroughouttheconversion.
BeforeIbegin,IwanttocommentonKirk'spreviousanswer.Heisabsolutelycorrectaslongasthere
arenoassumptionsmade.However,ifthedeveloperonlylooksforrepeatingpatternswithinthe
confinesoftheDecimalDataType.3333333...canberepresentedas1/3.Anexampleofthe
algorithmcanbefoundatbasicmathematics.com.Again,thismeansyouhavetomakeassumptions
basedontheinformationavailableandusingthismethodonlycapturesaverysmallsubsetof
repeatingdecimals.Howeverforsmallnumbersshouldbeokay.
Movingforward,letmegiveyouasnapshotofmysolution.Ifyouwanttoreadacompleteexample
withadditionalcodeIcreatedablogpostwithmuchmoredetail.
ConvertDecimalDataTypetoaStringFraction
publicstaticvoidDecimalToFraction(decimalvalue,refdecimalsign,refdecimalnumerator,
{
constdecimalmaxValue=decimal.MaxValue/10.0M;
//e.g..25/1=(.25*100)/(1*100)=25/100=1/4
vartmpSign=value<decimal.Zero?1:1;
vartmpNumerator=Math.Abs(value);
vartmpDenominator=decimal.One;
//Whilenumeratorhasadecimalvalue
while((tmpNumeratorMath.Truncate(tmpNumerator))>0&&
tmpNumerator<maxValue&&tmpDenominator<maxValue)
{
tmpNumerator=tmpNumerator*10;
tmpDenominator=tmpDenominator*10;
}
tmpNumerator=Math.Truncate(tmpNumerator);//JustincasemaxValueboundarywasreached.
ReduceFraction(reftmpNumerator,reftmpDenominator);
sign=tmpSign;
numerator=tmpNumerator;
denominator=tmpDenominator;
}
publicstaticstringDecimalToFraction(decimalvalue)
{
varsign=decimal.One;
varnumerator=decimal.One;
vardenominator=decimal.One;
DecimalToFraction(value,refsign,refnumerator,refdenominator);
returnstring.Format("{0}/{1}",(sign*numerator).ToString().TruncateDecimal(),
denominator.ToString().TruncateDecimal());
}
ThisisprettystraightforwardwheretheDecimalToFraction(decimalvalue)isnothingmorethana
simplifiedentrypointforthefirstmethodwhichprovidesaccesstoallthecomponentswhichcompose
afraction.Ifyouhaveadecimalof.325thendivideitby10tothepowerofnumberofdecimalplaces.
Lastlyreducethefraction.And,inthisexample.325=325/10^3=325/1000=13/40.
Next,goingtheotherdirection.
ConvertStringFractiontoDecimalDataType
staticreadonlyRegexFractionalExpression=newRegex(@"^(?<sign>[])?(?<numerator>\d+)(/(?<denominator>\d+))?$"
publicstaticdecimal?FractionToDecimal(stringfraction)
{
varmatch=FractionalExpression.Match(fraction);
if(match.Success)
{
//varsign=Int32.Parse(match.Groups["sign"].Value+"1");
varnumerator=Int32.Parse(match.Groups["sign"].Value+match.Groups["numerator"].Value
intdenominator;
if(Int32.TryParse(match.Groups["denominator"].Value,outdenominator))
returndenominator==0?(decimal?)null:(decimal)numerator/denominator;
if(numerator==0||numerator==1)
returnnumerator;
}
returnnull;
}
Convertingbacktoadecimalisquitesimpleaswell.Hereweparseoutthefractionalcomponents,
storetheminsomethingwecanworkwith(heredecimalvalues)andperformourdivision.
share improvethisanswer
editedMar1'12at20:01
answeredMar1'12at19:49
JeffWillener
629
addacomment
My2cents.Here'sVB.NETversionofbtilly'sexcellentalgorithm:
PublicSharedSubfloat_to_fraction(xAsDecimal,ByRefNumeratorAsLong,ByRefDenomAs
DimnAsLong=Int(Math.Floor(x))
x=n
Ifx<ErrMarginThen
Numerator=n
Denom=1
Return
ElseIfx>=1ErrMarginThen
Numerator=n+1
Denom=1
Return
EndIf
'Thelowerfractionis0/1
Dimlower_nAsInteger=0
Dimlower_dAsInteger=1
'Theupperfractionis1/1
Dimupper_nAsInteger=1
Dimupper_dAsInteger=1
Dimmiddle_n,middle_dAsDecimal
WhileTrue
'Themiddlefractionis(lower_n+upper_n)/(lower_d+upper_d)
middle_n=lower_n+upper_n
middle_d=lower_d+upper_d
'Ifx+error<middle
Ifmiddle_d*(x+ErrMargin)<middle_nThen
'middleisournewupper
upper_n=middle_n
upper_d=middle_d
'ElseIfmiddle<xerror
ElseIfmiddle_n<(xErrMargin)*middle_dThen
'middleisournewlower
lower_n=middle_n
lower_d=middle_d
'Elsemiddleisourbestfraction
share improvethisanswer
answeredDec6'12at15:32
dotNET
10.6k
41
84
addacomment
Well,seemsIfinallyhadtodoitmyself.IjusthadtocreateaprogramsimulatingthenaturalwayI
wouldsolveitmyself.Ijustsubmittedthecodetocodeprojectaswritingoutthewholecodeherewon't
besuitable.YoucandownloadtheprojectfromhereFraction_Conversion,orlookatthecodeproject
pagehere.
Here'showitworks:
1.Findoutwhethergivendecimalisnegative
2.Convertdecimaltoabsolutevalue
3.Getintegerpartofgivendecimal
4.Getthedecimalpart
5.Checkwhetherdecimalisrecurring.Ifdecimalisrecurring,wethenreturntheexactrecurring
decimal
6.Ifdecimalisnotrecurring,startreductionbychangingnumeratorto10^no.ofdecimal,elsewe
subtract1fromnumerator
7.Thenreducefraction
CodePreview:
privatestaticstringdec2frac(doubledbl)
{
charneg='';
doubledblDecimal=dbl;
if(dblDecimal==(int)dblDecimal)returndblDecimal.ToString();//returnnoifit'snotadecimal
if(dblDecimal<0)
{
dblDecimal=Math.Abs(dblDecimal);
neg='';
}
varwhole=(int)Math.Truncate(dblDecimal);
stringdecpart=dblDecimal.ToString().Replace(Math.Truncate(dblDecimal)+".","");
doublerN=Convert.ToDouble(decpart);
doublerD=Math.Pow(10,decpart.Length);
stringrd=recur(decpart);
intrel=Convert.ToInt32(rd);
if(rel!=0)
{
rN=rel;
rD=(int)Math.Pow(10,rd.Length)1;
}
//justafewprimefactorsfortestingpurposes
varprimes=new[]{41,43,37,31,29,23,19,17,13,11,7,5,3,2};
foreach(intiinprimes)reduceNo(i,refrD,refrN);
rN=rN+(whole*rD);
returnstring.Format("{0}{1}/{2}",neg,rN,rD);
}
Thanks@Dariusforgivenmeanideaofhowtosolvetherecurringdecimals:)
share improvethisanswer
editedJul16'15at22:41
Mike
1,278
answeredMar6'11at12:18
ChibuezeOpata
9
26
5,091
23
48
Whatwillyoudowithfractionsthathaverecurringdecimalsthatdonotrecurwithinaperiodthatfitsin
floatingpoint?Thathappensevenwithfairlymodestfractions.btillyJun13'13at19:42
@btilly:Thiswasalongtimeago,andwasjustafairlysimpleapproachtotheissueaswellasthebest
acceptablesolutionthen.AbettersolutionwouldbetousetheBigIntegerclass.ItworkedwithallfractionsI
testedwiththough,maybeyoucouldtryitoutyourselfwithsuchfractionsasyousuggest.
ChibuezeOpata Jun13'13at20:32
Idisagreeabout"bestacceptablesolution"whenmysolutionwaspostedbeforeyours,isshorter,was
upvotedmore,handlesfractionsthatyoursdoesnot,andprovablycomesupwiththebestpossiblefractionin
allcaseswhileyoursdoesnot.I'mnotsurewhatdefinitionof"best"you'reusing.btillyJun13'13at22:02
Ididappreciateyoursolution,butitwasnotinC#,neitherwasanyother.IfJeremy'ssolutionwasavailable
then,Iwouldhaveacceptedit. ChibuezeOpata Jun13'13at22:23
addacomment
Icomeupwithaverylateanswer.ThecodeistakenfromanarticlefromRichardspublishedin
1981andwrittenin c .
inlineunsignedintrichards_solution(doubleconst&x0,unsignedlonglong&num,unsignedlong
sign=my::sign(x0);
doubleg(std::abs(x0));
unsignedlonglonga(0);
unsignedlonglongb(1);
unsignedlonglongc(1);
unsignedlonglongd(0);
unsignedlonglongs;
unsignedintiter(0);
do{
s=std::floor(g);
num=a+s*c;
den=b+s*d;
a=c;
b=d;
c=num;
d=den;
g=1.0/(gs);
if(err>std::abs(sign*num/denx0)){returniter;}
}while(iter++<1e6);
std::cerr<<__PRETTY_FUNCTION__<<":failedtofindafractionfor"<<x0<<std::endl;
return0;
}
Irewriteheremyimplementationofbtilly_solution:
inlineunsignedintbtilly_solution(doublex,unsignedlonglong&num,unsignedlonglong&den
sign=my::sign(x);
num=std::floor(std::abs(x));
x=std::abs(x)num;
unsignedlonglonglower_n(0);
unsignedlonglonglower_d(1);
unsignedlonglongupper_n(1);
unsignedlonglongupper_d(1);
unsignedlonglongmiddle_n;
unsignedlonglongmiddle_d;
unsignedintiter(0);
do{
middle_n=lower_n+upper_n;
middle_d=lower_d+upper_d;
if(middle_d*(x+err)<middle_n){
upper_n=middle_n;
upper_d=middle_d;
}elseif(middle_d*(xerr)>middle_n){
lower_n=middle_n;
lower_d=middle_d;
}else{
num=num*middle_d+middle_n;
den=middle_d;
returniter;
}
}while(iter++<1e6);
den=1;
std::cerr<<__PRETTY_FUNCTION__<<":failedtofindafractionfor"<<x+num<<std::endl;
return0;
}
AndhereIproposesometestswithanerrorof 1e10 :
|
btilly0.1666670.166667=1/6in5iterations|1/6
richard0.1666670.166667=1/6in1iterations|
|
btilly0.3333330.333333=1/3in2iterations|1/3
richard0.3333330.333333=1/3in1iterations|
|
btilly0.1428570.142857=1/7in6iterations|1/7
richard0.1428570.142857=1/7in1iterations|
|
btilly0.7142860.714286=5/7in4iterations|5/7
richard0.7142860.714286=5/7in4iterations|
|
btilly1e071.001e07=1/9990010in9990009iteration|0.0000001
richard1e071e07=1/10000000in1iterations|
|
btilly3.666673.66667=11/3in2iterations|11/3
richard3.666673.66667=11/3in3iterations|
|
btilly1.414211.41421=114243/80782in25iterations|sqrt(2)
richard1.414211.41421=114243/80782in13iterations|
|
btilly3.141593.14159=312689/99532in317iterations|pi
richard3.141593.14159=312689/99532in7iterations|
|
btilly2.718282.71828=419314/154257in36iterations|e
richard2.718282.71828=517656/190435in14iterations|
|
btilly0.3908850.390885=38236/97819in60iterations|random
richard0.3908850.390885=38236/97819in13iterations|
Asyoucansee,thetwomethodsgivemoreorlessthesameresultsbuttherichards'oneisway
moreefficientandeasiertoimplement.
Edit
Tocompilemycodeyouneedadifinitionfor my::sign whichissimplyafunctionthatreturnsthesign
ofavariable.Hereismyimplementation
namespacemy{
template<typenameType>inlineconstexpr
intsign_unsigned(Typex){returnType(0)<x;}
template<typenameType>inlineconstexpr
intsign_signed(Typex){return(Type(0)<x)(x<Type(0));}
template<typenameType>inlineconstexpr
intsign(Typex){returnstd::is_signed<Type>()?sign_signed(x):sign_unsigned(x);}
}
Sorry
Iguessthisanswerreferstothesamealgorithm.Ididn'tseethatbefore...
share improvethisanswer
editedJun1at16:06
answeredJun1at15:57
PinkFloyd
537
addacomment
17
addacomment
Here'sanalgorithmimplementedinVBthatconvertsFloatingPointDecimaltoIntegerFractionthatI
wrotemanyyearsago.
Basicallyyoustartwithanumerator=0andadenominator=1,thenifthequotientislessthanthe
decimalinput,add1tothenumeratorandifthequotientisgreaterthanthedecimalinput,add1tothe
denominator.Repeatuntilyougetwithinyourdesiredprecision.
share improvethisanswer
answeredFeb26'11at10:20
oosterwal
1,238
16
addacomment
IfIwereyouI'dhandlethe"norepeatingdecimalsin.NET"problembyhavingitconvertstringswith
therecurrencemarkedsomehow.
E.g.1/3couldberepresented"0.R3"1/60couldberepresented"0.01R6"
I'drequireanexplicitcastfromdoubleordecimalbecausesuchvaluescouldonlybeconvertedintoa
fractionthatwasclose.Implicitcastfromintisok.
Youcoulduseastructandstoreyourfraction(f)intwolongspandqsuchthatf=p/q,q!=0,and
gcd(p,q)==1.
share improvethisanswer
answeredJun24'13at16:29
ChrisSusie
19
addacomment
Here,youcanhavethemethodforconvertingDecimalintoFractions:
///<summary>
///ConvertsDecimalsintoFractions.
///</summary>
///<paramname="value">Decimalvalue</param>
///<returns>Fractioninstringtype</returns>
publicstringDecimalToFraction(doublevalue)
{
stringresult;
doublenumerator,realValue=value;
intnum,den,decimals,length;
num=(int)value;
value=valuenum;
value=Math.Round(value,5);
length=value.ToString().Length;
decimals=length2;
numerator=value;
for(inti=0;i<decimals;i++)
{
if(realValue<1)
{
numerator=numerator*10;
}
else
{
realValue=realValue*10;
numerator=realValue;
}
}
den=length2;
stringten="1";
for(inti=0;i<den;i++)
{
ten=ten+"0";
}
den=int.Parse(ten);
num=(int)numerator;
result=SimplifiedFractions(num,den);
returnresult;
share improvethisanswer
answeredJan21at14:21
SyedFaizanAli
11