Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-10-2018 05:15 PM
Given an integer reciprocal, analyzes the decimal representation to identify the transient and repeating/recurring parts of the decimal.
Eg
1/3 has no transient and 3 repeats.
1/4 has transient 0.25 and no repetition.
1/7 has no transient and 0.142857 recurring.
1/14 has transient 0 and 6 recurring digits.
See http://www.hpmuseum.org/forum/thread-9919.html
For a description of the algorithms and more examples.
(PPL) Version 0.2 follows the sequence of steps in Joe Horn's algorithm using MultiplicateOrder, which can be found here: http://www.hpmuseum.org/forum/thread-3212.html
Output is currently a list:
1: {Length of transient, transient}
2: {Length of recurring part, both parts but omitting the 0. Or 1.}
3: Just as a check, the real value.
The formatting could be improved.
Note that if the length of the recurring part exceeds 12 the recurring part returned is unreliable and should probably be replaced by NaN (the indicated length is OK, but not the digits...and sometimes they are not digits).
Version 0.3 includes an optimised MultiplicativeOrder (base 10) and outputs a string with the recurring part annotated with an underscore (selectable).
Note there is a bug with the string formatting...Sometimes it includes the integer part, sometimes not.
Version 0.4 has cosmetic improvements and help. It adds a CAS variable which show more digits, but I have yet to find out how use that in PPL - Whenever I try to turn it into a string, it turns into a real instead.
(Update: I have found the required syntax to do that now, so expect another release soon.)
Version 0.5 is a much improved PPL program for showing transient parts and repetends of reciprocals. It has been rewritten to use CAS, enabling Transients and Repetends >12 digits to be handled, and is faster.
Version 0.6
Show optimised. Note that the transient display assumes USCORE (underscore) is not empty.
Implementation Limits added (Some large values eg NN=1E14 exceed the limits of euler).
In V0.7, 1/1E14 is reduced to 1E-14/1 and non-integer numerator is processed, resulting in a NaN with no error message from MultiplicativeOrder10 seen.
Potentially, there might be other values which MultiplicateOrder10 might have handled, but which are not being processed because the reduction step is introducing unexpected non-integer values.
Version 0.8
Improved handling of implementation limitations.
Improved user interface shows more digits on-screen and allows long strings not to be generated.
Version 0.9 fixes a bug caused by "exact", introduced in V0.8. Thanks for spotting that.
Error MSGBOX is improved (using an AFile to save/restore the screen).
Code:
LOCAL CRID:="REPEATING DECIMAL V 0.9 \n©2018 StephenG1CMZ";
LOCAL ALGM:="Following Joe Horn's algorithm: http://www.hpmuseum.org/forum/thread-9919.html";
//Written 2018 StephenG1CMZ
//Following Joe Horn`s algorithm using Multiplicative Order
//This ppl implement yields useful results using CAS
//to provide Transients and Repetends>12 digits
LOCAL FR2,FR5;//GLOBAL RETURN
LOCAL WHATSLEFT,NDIGITS;//GLOBAL RETURN
LOCAL ImpPRINT:=2000;//PRINT LIMIT <2K
LOCAL ImpSTRING:=64000;//LIMIT VALUE TBC
EXPORT RedNUMER,RedDENOM;
EXPORT TransientLEN;//Length of Transient
EXPORT RepetendLEN;//Length of Recurring Digits/Repeating Digits
//^Len == Decimal Digits Only
EXPORT USCORE:="_";//CHANGE TO EMPTY STRING IF YOU WISH TO EVAL THE STRING
EXPORT WAITS:=0;
EXPORT ImpRELEN:=ImpSTRING;//MAX LEN TO GENERATE
EXPORT TESTED:=0;
EXPORT BigT:=0;
EXPORT BigR:=0;
LOCAL STNAN:="(NaN)";
LOCAL DIVBY0:=STNAN+"{0}";//DIVBY0
LOCAL NOTINT:=STNAN+"(NotInteger)";
LOCAL STNANImp:=STNAN+"(Imp)";//Implemenation Limit
LOCAL FL:="ZTmpMESSAGEBOX";
LOCAL ImpLimST:="MultiplicativeOrder10: Implementation Limit!\n";
MultiplicativeOrder10();//FORWARD
EXPORT ABOUT()
BEGIN
PRINT();
PRINT(CRID);
PRINT(ALGM);
END;
EXPORT HELP()
BEGIN
PRINT();
PRINT(CRID);
PRINT(" Discovers characteristics of rational fractions that can be specified by an integer numerator and denominator.");
PRINT(" ");
PRINT("NB Fractions are input as 2 integers.\nDo not enter as Prime fractions.");
PRINT(" ");
PRINT("Scroll or Esc...");
PRINT(" ");
PRINT("API functions:");
PRINT(" ");
PRINT("GetLEN(fraction):");
PRINT("Returns lengths and Sets LENgth variables.\nLengths exclude the IP and non-digits.");
PRINT("GetLEN(1,3)={0,1}");
PRINT(" ");
PRINT("Recurring(fraction):");
PRINT("Repeating(fraction):");
PRINT("Returns a string of the real value with _ marking any recurring digits.");
PRINT("Recurring(1,3)=0._3");
PRINT(" ");
PRINT("MultiplicativeOrder10(WhatsLeft):");
PRINT("Implemented for base 10.");
PRINT(" ");
PRINT("Procedures:");
PRINT(" ");
PRINT("Show(fraction):");
PRINT("Show Representations on_screen");
PRINT("Key [Esc] to continue to next page");
PRINT("Key [View] for more digits");
PRINT("(Do not tap)");
PRINT("Show(1,3)");
PRINT(" ");
PRINT("ShowRange(Numer1,Numer2,Denom1,Denom2):");
PRINT("Show Range of fractions one by one\nShowRange(1,0,3,0) = Show(1,3)");
PRINT("ShowRange(1,2,3,4)=Show(1,3),Show(1,4),Show(2,3),Show(2,4)");
PRINT(" ");
PRINT("ShowReciprocals(Denom1,Denom2):");
PRINT("Shows a range of reciprocal values one by one.\nShowReciprocals(3,0)=Show(1,3)");
PRINT("ShowReciprocals(1,3)=Show(1,1),Show(1,2),Show(1,3)");
PRINT("");
PRINT("Variables In: (Selectable by user @Home)");
PRINT("ImpRELEN: Recurring LEN limit");//Limit to manageable length
PRINT("String USCORE: Underscore ");
PRINT("WAITS: Show WAIT time (0=Esc)");
PRINT(" ");
PRINT("Variables Out:");
PRINT("RepetendLEN, TransientLEN//Lengths");
PRINT("RedNUMER, RedDENOM//Reduced");
PRINT("(set by GetLEN,Recurring,Repeating)");
PRINT(" ");
PRINT("Known Limitations:");
PRINT("Numbers beyond about 1/12109:");
PRINT("Repetend digits incorrectly show just 0-s (LEN OK)");
PRINT("Negative inputs may error.");
PRINT(" ");
PRINT("Hint: To PRINT long strings,");
PRINT("PRINT(a few bytes at a time)");
PRINT(" ");
PRINT("Interesting Test Values:");
PRINT("{1/: 0,1,2,3,4,7,14,208,301,8192}");
PRINT("{1/: 29989}//WRONG}");
PRINT("{1/: 1ᴇ14}//Imp");
PRINT("{1/: 3^15}//ImpCRASH AVOIDED");
PRINT("{2/: 3}");
PRINT("{2/: 16384}");
PRINT("{123/208}");
END;
//STANDARD ROUTINES
DecSepNow()
//RETURN current system decimal separator
//From the forum
BEGIN
RETURN IFTE((HSeparator MOD 9)<4,".",",");
END;
DIGITSNEEDED(NN)
//DIGITS NEEDED FOR IP
//EXCLUDES SIGN POINT SEPARATOR
BEGIN
//RETURN IP(LOG(MAX(1,ABS(NN))))+1;//PORTABLE
RETURN MAX(0,(XPON(NN)))+1;//PRIME XPON=BASE 10
END;
SCR_GET()
BEGIN
G0:=AFiles(FL)
END;
SCR_PUT()
BEGIN
AFiles(FL):=G0;
END;
ZMSGBOX(ST,OKC)
BEGIN
SCR_PUT();
OKC:=MSGBOX(ST,OKC);
SCR_GET();
RETURN OKC;
END;
ZEROS(NN)
//Recursive is faster than using CAS
//and can output more zeros than PPL
BEGIN
//MSGBOX(NN);
IF NN>0 THEN
RETURN "0"+ZEROS(NN-1);
END;
RETURN "";
END;
//END STANDARD
MaxFactor25(NN)
BEGIN
LOCAL MAXFACTR:=0;
LOCAL LST,LSTB,LP,II;
LST:=mat2list(ifactors(NN)); //BASES AND COUNTSETC
//EXTRACT BASES (2,5) ETC
LSTB:=IFTE(SIZE(LST),MAKELIST(LST(2*II-1),II,1,(SIZE(LST)/2)),{});//{} HANDLES NN=1
FR2:=0; FR5:=0;
LP:=POS(LSTB,2);
IF LP THEN
FR2:=LST(2*LP);//EXPONENT
END;
LP:=POS(LSTB,5);
IF LP THEN
FR5:=LST(2*LP);//EXPONENT
END;
MAXFACTR:=MAX(FR2,FR5);
RETURN MAXFACTR;//0=NONE
END;
EXPORT GetLEN(NumerN,DenomN)
//Get TransientLEN,RepetendLEN,NDIGITS
//These lengths are decimal places and exclude IPLEN
//0 DENOMINATOR: RETURN {0,0,0}
//NEGATIVES:UNCHECKED
BEGIN
LOCAL GD;
IF DenomN THEN
//Step 0: Reduce Fraction
GD:=gcd(NumerN,DenomN);
RedNUMER:=NumerN/GD;
RedDENOM:=DenomN/GD;
//Continue
TransientLEN:=MaxFactor25(RedDENOM);
WHATSLEFT:=exact(RedDENOM/(2^FR2*5^FR5));
RepetendLEN:=MultiplicativeOrder10(WHATSLEFT);
NDIGITS:=TransientLEN+RepetendLEN;
//PRINT({TransientLEN,RepetendLEN});
ELSE //DIVBY0
TransientLEN:=0;
RepetendLEN:=0;
NDIGITS:=0;
END;
//NOTE: 0,0 IS OFTEN SUGGESTIVE OF AN ERROR
//BUT IS ALSO RETURNED BY 1/1
//SO MAYBE USE AN ERRORFLAG?
RETURN {TransientLEN,RepetendLEN,NDIGITS};
END;
ReDIGITS (NUMER,NN)
//Determine Digits of the fraction (NUMER/NN)
//NUMERATOR INTEGER
//DENOMINATOR INTEGER
//NEGATIVES:ATTEMPTED BUT RAISE ERROR
//DENOMINATOR 0: RETURN NAN
//NONINTEGER: RETURN NAN
BEGIN
LOCAL GD;
LOCAL LST;//UNUSED
LOCAL ST:="";//STRINGVERSION
LOCAL STR:="";
LOCAL TRANSIENTPART,REPETEND,INTPART;
LOCAL RESULTS;
LOCAL DP;//DECIMAL PLACES
LOCAL IPLEN;
//MSGBOX({NUMER,NN});
//GUARDS
IF NN==0 THEN
RETURN DIVBY0;
END;
IF FP(NUMER) OR FP(NN) THEN
RETURN NOTINT+STRING({NUMER,NN});//PARAMETERS NOT INTEGER
END;
IF NUMER<0 OR NN<0 THEN
RESULTS:=ReDIGITS(ABS(NUMER),ABS(NN));
RETURN IFTE((NUMER/NN)<0,"−"+RESULTS,RESULTS);
END;
//OK
//Step 0: reduce fraction
GD:=gcd(NUMER,NN);
RedNUMER:=NUMER/GD;
RedDENOM:=NN/GD;
//Continue...
//Get Length
//A possible optimisation:
//Avoid recalc
LST:=GetLEN(RedNUMER,RedDENOM);
//IF EXCESSIVE LENGTH RETURN NAN
IF NDIGITS>MIN(ImpRELEN,ImpSTRING-9) THEN //−9-ALLOW FOR DECSEP ETC
//STRING:TOO LONG TO GENERATE
//RELEN:USER LIMIT(TOO LONG/SLOW FOR USER)
RETURN STNANImp;
END;
//GetDecimalPlaces
//RP:=iquo((1*10^NDIGITS),NN);//works for repetends≤12digits:so use CAS
CAS("temporary:=iquo(((RedNUMER*10^NDIGITS),RedDENOM)");
ST:=CAS("string(temporary)");
//MSGBOX(ST);
STR:=ZEROS(NDIGITS-DIM(ST));
DP:=STR+ST;
//GetINTEGERPART
INTPART:=IFTE(RedDENOM,(IP(RedNUMER/RedDENOM)+DecSepNow()),DIVBY0);//THIS DIVBY IS GUARDED
//IF NUMER==1 THEN
//RECIPROCAL:iquo returns decimal places only
IF RedNUMER≠1 THEN //NN
//DEBUG;
//FRACTION: iquo returns IP and decimals with no point
IPLEN:=DIGITSNEEDED((IP(RedNUMER/RedDENOM)));//LEN OF IP
DP:=MID(DP,IPLEN+1); //DP
END;
//GetRepetend
REPETEND:=IFTE(RepetendLEN,MID(DP,TransientLEN+1),"");
//GetTransientDecimalPlaces
TRANSIENTPART:=IFTE(TransientLEN,LEFT(DP,TransientLEN),"");
RESULTS:=INTPART+TRANSIENTPART+USCORE+REPETEND;
//PRINT("RESULTS: "+RESULTS);
//Return a formatted string
//(Relevant Lengths are returned globally in ...LEN variables)
RETURN RESULTS;
END;
EXPORT Recurring(NumerN,DenomN)
BEGIN
RETURN ReDIGITS(NumerN,DenomN);
END;
//Some call them Recurring
//Some call them Repeating
EXPORT Repeating(NumerN,DenomN)
BEGIN
RETURN ReDIGITS(NumerN,DenomN);
END;
GetTransient(NUMER,NN)
//NB Only when USCORE NOT EMPTY
//NB RECALCULATES
BEGIN
LOCAL ST:=ReDIGITS(NUMER,NN);
RETURN LEFT(ST,INSTRING(ST,USCORE));
END;
GetTransientToo(NUMER,NN,ST)
//Requires ST to supply digits an4 USCORE
BEGIN
RETURN LEFT(ST,INSTRING(ST,USCORE));
END;
EXPORT Show(NumerN,DenomN)
BEGIN
LOCAL PXP:={0};
LOCAL TRANSIENTPART;
LOCAL DST;
LOCAL KK,LST;
RECT_P();
TEXTOUT_P(CRID,0,0,3);
//TEXTOUT_P(ALGM,0,15,1);
TEXTOUT_P("Fraction,Reduced Fraction,Real,Decimal:",0,40);
// COMMON REPRESENTATIONS
TEXTOUT_P(NumerN+"/"+DenomN,0,60);
TEXTOUT_P(IFTE(DenomN,(NumerN/DenomN),(DIVBY0))+" Real",0,100);
//EARLY LENGTH
//Note:This is recalculated in ReDIGITS
LST:=GetLEN(NumerN,DenomN);
TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);
TEXTOUT_P(RedNUMER+"/"+RedDENOM+" Reduced Fraction",0,80);
//Main Display
DST:=Recurring(NumerN,DenomN);
//TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);//IF NOT EARLY
PXP(0):=TEXTOUT_P(DST+" Decimal",0,120);
TRANSIENTPART:=GetTransientToo(NumerN,DenomN,DST);
PXP(0):=TEXTOUT_P(TRANSIENTPART+" Transient Part",0,140);
IF INSTRING(DST,"00000000000000") THEN
//Loss Of significance suspected
TEXTOUT_P(" ! Check Possible Loss Of Significance !",0,200,3,RGB(255,0,0));
END;
IF MAX(PXP)≥320 THEN //OFFSCREEN
TEXTOUT_P("[View]",320*(4/6),220,3,RGB(255,0,0));
END;
TEXTOUT_P(IFTE(WAITS>0,"Wait...","[Esc]"),320*(5/6),220,3);
KK:=WAIT(WAITS);
IF KK==9 THEN //VIEW KEY
PRINT();
PRINT(NumerN+"/"+DenomN);
PRINT(LST);
PRINT(MID(DST,1,ImpPRINT));
IF DIM(DST)>ImpPRINT THEN
PRINT("PRINT Unable");//Or:Loop
END;
//WAIT(WAITS);
END;
RETURN DST;
END;
EXPORT ShowRange(Numer1,Numer2,Denom1,Denom2)
//Show range one by one
BEGIN
LOCAL II,JJ;
FOR II FROM Numer1 TO MAX(Numer2,Numer1) DO
FOR JJ FROM Denom1 TO MAX(Denom2,Denom1) DO
Show(II,JJ);
END;
END;
END;
EXPORT ShowReciprocals(FIRST,LAST)
BEGIN
LOCAL NN;
FOR NN FROM FIRST TO MAX(LAST,FIRST) DO
Show(1,NN);
END;
END;
EXPORT MultiplicativeOrder10(WhatsLeft)
//See http://www.hpmuseum.org/forum/thread-3212.html
// WHATSLEFT
BEGIN
LOCAL OKC:=0;
LOCAL AP:=0;
LOCAL NOTFND:=1;
LOCAL LST:={};//POSSIBLES TO CHECK
IF WhatsLeft≠1 THEN
//Get possibles
LST:=mat2list(idivis(euler(WhatsLeft)));
IF TYPE(LST)≠TYPE({}) THEN //EMPIRICAL TEST
//EG NN=1ᴇ14
OKC:=ZMSGBOX((ImpLimST+LST),OKC);
//PERHAPS RETURN AND HANDLE NAN INSTEAD
//IF NOT CAUGHT powmod WILL FAIL
END;
IF TYPE(LST)==TYPE({}) THEN
//Search possiblesfor a match
WHILE AP<SIZE(LST) AND NOTFND DO
AP:=AP+1;
IF powmod(10,LST(AP),WhatsLeft)==1 THEN
NOTFND:=0;//FOUND
END; //IF
END;//LOOP
END;//IF
//Return match
END;//VALID LST
//Return 0 if WL=1 or no match
RETURN IFTE(NOTFND,0,LST(AP));
END;
EXPORT TESTS(TESTN)
//Perform some tests
BEGIN
LOCAL III,RR;
FOR III FROM 1 TO TESTN DO
//RECT_P();
//TEXTOUT_P(III,0,120);
TESTED:=III;
RR:=ReDIGITS(2,III);
//Note the biggest
IF TransientLEN>BigT THEN
BigT:=TransientLEN;
END;
IF RepetendLEN>BigR THEN
BigR:=RepetendLEN;
END;
DRAWMENU(III,BigT,BigR);//MAX
//DRAWMENU(III,TransientLEN,RepetendLEN);//CURRENT
END;
END;
TESTD()
BEGIN
LOCAL II;
FOR II FROM 1 TO 1000 DO
DIGITSNEEDED(123.4);
END;
END;
EXPORT REDECIMALS()
BEGIN
LOCAL TESTN:=1000;
ABOUT();
//HEADLINE RATE: LARGER WILL BE SLOWER
PRINT(TESTN+" fractions in "+TEVAL(TESTS(TESTN)));
//PRINT(DIM(Recurring(1,29989)));
//HELP();
END;
Some long repetends continue to incorrectly show all zeros.
Negative fractions may now cause a MultiplicativeOrder10 error message
Update: Some values are incorrect...
|