HP Forums

Full Version: Round a decimal value to fraction using Farey series
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Round Vl to best fraction with denominator smaller than DMax
Code:
EXPORT FareyMax(Vl, DMax)
// Round a Vl to the best fraction with denominator < DMax
BEGIN
LOCAL VlE, Tmp;
LOCAL DbN,DbD, FnN,FnD, RsN,RsD,RsE;
VlE:= ABS(Vl);
DbN:= IP(VlE); DbD:=1; FnN:=DbN+1; FnD:=1;
RsN:= ROUND(VlE,0); RsD:= 1; RsE:= ABS(VlE-(RsN/RsD));
WHILE DbD+FnD <= DMax DO
  Tmp:= (DbN+FnN)/(DbD+FnD);
  IF RsE > ABS(VlE-Tmp) THEN RsN:= (DbN+FnN); RsD:= (DbD+FnD); RsE:= ABS(VlE-(RsN/RsD)); END;
  IF Tmp < VlE THEN DbN:= (DbN+FnN); DbD:= (DbD+FnD); ELSE FnN:= (DbN+FnN); FnD:= (DbD+FnD); END;
END;
RETURN "'"+SIGN(Vl)*RsN+"/"+RsD+"'";
END;

Round Vl to best fraction with Vl-Error < Fraction < Vl+Error
Code:
EXPORT FareyDelta(Vl, Error)
// round Vl to the smallest fraction with |Vl - fraction| < Error
BEGIN
LOCAL VlE, Tmp;
LOCAL DbN,DbD, FnN,FnD, RsN,RsD,RsE;
VlE:= ABS(Vl);
DbN:= IP(VlE); DbD:=1; FnN:=DbN+1; FnD:=1;
RsN:= ROUND(VlE,0); RsD:= 1; RsE:= ABS(VlE-(RsN/RsD));
WHILE RsE > Error DO
  Tmp:= (DbN+FnN)/(DbD+FnD);
  IF RsE > ABS(VlE-Tmp) THEN RsN:= (DbN+FnN); RsD:= (DbD+FnD); RsE:= ABS(VlE-(RsN/RsD)); END;
  IF Tmp < VlE THEN DbN:= (DbN+FnN); DbD:= (DbD+FnD); ELSE FnN:= (DbN+FnN); FnD:= (DbD+FnD); END;
END;
RETURN "'"+SIGN(Vl)*RsN+"/"+RsD+"'";
END;
FYI: The INT (integer-part) function no longer exists in the current firmware. You must replace INT with IP for the above programs to work.
Ooops, forgot to recheck before posting.
Done. Note the simplified RETURN
Here is an update.
The program is made for Home but should work in CAS
It list all successives fractions in terminal as they get better
Exemple: FareyMax(π;10000)
Code:
EXPORT FareyMax(Vl, DMax)
// Round a Vl to the best fraction with denominator < DMax
BEGIN
LOCAL VlE, Tmp;
LOCAL DbN,DbD, FnN,FnD, RsN,RsD,RsE;
PRINT();
VlE:= ABS(Vl);
DbN:= IP(VlE); DbD:=1; FnN:=DbN+1; FnD:=1;
RsN:= ROUND(VlE,0); RsD:= 1; RsE:= ABS(VlE-(RsN/RsD));
PRINT(SIGN(Vl)*RsN+"/"+RsD);
WHILE DbD+FnD <= DMax DO
  Tmp:= (DbN+FnN)/(DbD+FnD);
  IF RsE > ABS(VlE-Tmp) THEN RsN:= (DbN+FnN); RsD:= (DbD+FnD); RsE:= ABS(VlE-(RsN/RsD)); PRINT(SIGN(Vl)*RsN+"/"+RsD);END;
  IF Tmp < VlE THEN DbN:= (DbN+FnN); DbD:= (DbD+FnD); ELSE FnN:= (DbN+FnN); FnD:= (DbD+FnD); END;
END;
RETURN "'"+SIGN(Vl)*RsN+"/"+RsD+"'";
// RETURN EXPR("QUOTE("+SIGN(Vl)*RsN+"/"+RsD+")");
END;
Update for Rev 6030
Code:
#pragma mode( separator(.,;) integer(h64) )
// rev 6030 update
EXPORT RoundFract(V, F)
BEGIN
RETURN ROUND(V*F,0)/F;
END;

EXPORT FareyMax(Vl, DMax)
// Round a Vl to the best fraction with denominator < DMax
BEGIN
LOCAL VlE, Tmp;
LOCAL DbN,DbD, FnN,FnD, RsN,RsD,RsE;
PRINT();
VlE:= ABS(Vl);
DbN:= IP(VlE); DbD:=1; FnN:=DbN+1; FnD:=1;
RsN:= ROUND(VlE,0); RsD:= 1; RsE:= ABS(VlE-(RsN/RsD));
PRINT(STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0));
WHILE DbD+FnD <= DMax DO
  Tmp:= (DbN+FnN)/(DbD+FnD);
  IF RsE > ABS(VlE-Tmp) THEN RsN:= (DbN+FnN); RsD:= (DbD+FnD); RsE:= ABS(VlE-(RsN/RsD)); PRINT(STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0));END;
  IF Tmp < VlE THEN DbN:= (DbN+FnN); DbD:= (DbD+FnD); ELSE FnN:= (DbN+FnN); FnD:= (DbD+FnD); END;
END;
// RETURN "'"+STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0)+"'";
RETURN EXPR("QUOTE("+STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0)+")");
END;

EXPORT FareyDelta(Vl, Error)
// round Vl to the smallest fraction with |Vl - fraction| < Error
BEGIN
LOCAL VlE, Tmp;
LOCAL DbN,DbD, FnN,FnD, RsN,RsD,RsE;
VlE:= ABS(Vl);
DbN:= IP(VlE); DbD:=1; FnN:=DbN+1; FnD:=1;
RsN:= ROUND(VlE,0); RsD:= 1; RsE:= ABS(VlE-(RsN/RsD));
WHILE RsE > Error DO
  Tmp:= (DbN+FnN)/(DbD+FnD);
  IF RsE > ABS(VlE-Tmp) THEN RsN:= (DbN+FnN); RsD:= (DbD+FnD); RsE:= ABS(VlE-(RsN/RsD)); END;
  IF Tmp < VlE THEN DbN:= (DbN+FnN); DbD:= (DbD+FnD); ELSE FnN:= (DbN+FnN); FnD:= (DbD+FnD); END;
END;
// RETURN "'"+STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0)+"'";
RETURN EXPR("QUOTE("+STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0)+")");
END;

EXPORT FareyDisp(Vl,Rnd)
// round Vl to the smallest fraction that match the display
BEGIN
LOCAL VlE, Tmp;
LOCAL DbN,DbD, FnN,FnD, RsN,RsD,RsE;
VlE:= ABS(ROUND(Vl,Rnd));
DbN:= IP(VlE); DbD:=1; FnN:=DbN+1; FnD:=1;
RsN:= ROUND(VlE,0); RsD:= 1; RsE:= ABS(VlE-ROUND(RsN/RsD,Rnd));
WHILE RsE > 0 DO
  Tmp:= (DbN+FnN)/(DbD+FnD);
  IF RsE > ABS(VlE-Tmp) THEN RsN:= (DbN+FnN); RsD:= (DbD+FnD); RsE:= ABS(VlE-ROUND(RsN/RsD,Rnd)); END;
  IF Tmp < VlE THEN DbN:= (DbN+FnN); DbD:= (DbD+FnD); ELSE FnN:= (DbN+FnN); FnD:= (DbD+FnD); END;
END;
RETURN EXPR("QUOTE("+STRING(SIGN(Vl)*RsN,2,0)+"/"+STRING(RsD,2,0)+")");
END;
Nota: if you want FareyMax to be a simple function, just remove all PRINT commands from source code.
This is great, Patrice!
Corrected a typo in my last message (in function FareyDelta)
Reference URL's