Arithmetic/Rational

From Rosetta Code
Task
Arithmetic/Rational
You are encouraged to solve this task according to the task description, using any language you may know.
Task

Create a reasonably complete implementation of rational arithmetic in the particular language using the idioms of the language.


Example

Define a new type called frac with binary operator "//" of two integers that returns a structure made up of the numerator and the denominator (as per a rational number).

Further define the appropriate rational unary operators abs and '-', with the binary operators for addition '+', subtraction '-', multiplication '×', division '/', integer division '÷', modulo division, the comparison operators (e.g. '<', '≤', '>', & '≥') and equality operators (e.g. '=' & '≠').

Define standard coercion operators for casting int to frac etc.

If space allows, define standard increment and decrement operators (e.g. '+:=' & '-:=' etc.).

Finally test the operators: Use the new type frac to find all perfect numbers less than 219 by summing the reciprocal of the factors.


Related tasks



Action!

Calculations on a real Atari 8-bit computer take quite long time. It is recommended to use an emulator capable with increasing speed of Atari CPU.

INCLUDE "D2:REAL.ACT" ;from the Action! Tool Kit

TYPE Frac=[INT num,den]

REAL half

PROC PrintFrac(Frac POINTER x)
  PrintI(x.num) Put('/) PrintI(x.den)
RETURN

INT FUNC Gcd(INT a,b)
  INT tmp

  IF a<b THEN
    tmp=a a=b b=tmp
  FI

  WHILE b#0
  DO
    tmp=a MOD b
    a=b 
    b=tmp
  OD
RETURN (a)

PROC Init(INT n,d Frac POINTER res)
  IF d>0 THEN
    res.num=n res.den=d
  ELSEIF d<0 THEN
    res.num=-n res.den=-d
  ELSE
    Print("Denominator cannot be zero!")
    Break()
  FI
RETURN

PROC Assign(Frac POINTER x,res)
  Init(x.num,x.den,res)
RETURN

PROC Neg(Frac POINTER x,res)
  Init(-x.num,x.den,res)
RETURN

PROC Inverse(Frac POINTER x,res)
  Init(x.den,x.num)
RETURN

PROC Abs(Frac POINTER x,res)
  IF x.num<0 THEN
    Neg(x,res)
  ELSE
    Assign(x,res)
  FI
RETURN

PROC Add(Frac POINTER x,y,res)
  INT common,xDen,yDen
  
  common=Gcd(x.den,y.den)
  xDen=x.den/common
  yDen=y.den/common
  Init(x.num*yDen+y.num*xDen,xDen*y.den,res)
RETURN

PROC Sub(Frac POINTER x,y,res)
  Frac n

  Neg(y,n) Add(x,n,res)
RETURN

PROC Mult(Frac POINTER x,y,res)
  Init(x.num*y.num,x.den*y.den,res)
RETURN

PROC Div(Frac POINTER x,y,res)
  Frac i

  Inverse(y,i) Mult(x,i,res)
RETURN

BYTE FUNC Greater(Frac POINTER x,y)
  Frac diff

  Sub(x,y,diff)
  IF diff.num>0 THEN
    RETURN (1)
  FI
RETURN (0)

BYTE FUNC Less(Frac POINTER x,y)
RETURN (Greater(y,x))

BYTE FUNC GreaterEqual(Frac POINTER x,y)
  Frac diff

  Sub(x,y,diff)
  IF diff.num>=0 THEN
    RETURN (1)
  FI
RETURN (0)

BYTE FUNC LessEqual(Frac POINTER x,y)
RETURN (GreaterEqual(y,x))

BYTE FUNC Equal(Frac POINTER x,y)
  Frac diff

  Sub(x,y,diff)
  IF diff.num=0 THEN
    RETURN (1)
  FI
RETURN (0)

BYTE FUNC NotEqual(Frac POINTER x,y)
  IF Equal(x,y) THEN
    RETURN (0)
  FI
RETURN (1)

INT FUNC Sqrt(INT x)
  REAL r1,r2

  IF x=0 THEN
    RETURN (0)
  FI
  IntToReal(x,r1)
  Power(r1,half,r2)
RETURN (RealToInt(r2))

PROC Main()
  DEFINE MAXINT="32767"
  INT i,f,max2
  Frac sum,tmp1,tmp2,tmp3,one

  Put(125) PutE() ;clear screen
  ValR("0.5",half)
  Init(1,1,one)
  FOR i=2 TO MAXINT
  DO
    Init(1,i,sum) ;sum=1/i
    max2=Sqrt(i)
    FOR f=2 TO max2
    DO
      IF i MOD f=0 THEN
        Init(1,f,tmp1)     ;tmp1=1/f
        Add(sum,tmp1,tmp2) ;tmp2=sum+1/f
        Init(f,i,tmp3)     ;tmp3=f/i
        Add(tmp2,tmp3,sum) ;sum=sum+1/f+f/i
      FI
    OD

    IF Equal(sum,one) THEN
      PrintF("%I is perfect%E",i)
    FI
  OD
RETURN
Output:

Screenshot from Atari 8-bit computer

6 is perfect
28 is perfect
496 is perfect
8128 is perfect

Ada

[This section is included from a subpage and should be edited there, not here.]

The generic package specification:

generic
   type Number is range <>;
package Generic_Rational is
   type Rational is private;
   
   function "abs"   (A : Rational) return Rational;
   function "+"     (A : Rational) return Rational;
   function "-"     (A : Rational) return Rational;
   function Inverse (A : Rational) return Rational;
   
   function "+" (A : Rational; B : Rational) return Rational;
   function "+" (A : Rational; B : Number  ) return Rational;
   function "+" (A : Number;   B : Rational) return Rational;

   function "-" (A : Rational; B : Rational) return Rational;
   function "-" (A : Rational; B : Number  ) return Rational;
   function "-" (A : Number;   B : Rational) return Rational;

   function "*" (A : Rational; B : Rational) return Rational;
   function "*" (A : Rational; B : Number  ) return Rational;
   function "*" (A : Number;   B : Rational) return Rational;

   function "/" (A : Rational; B : Rational) return Rational;
   function "/" (A : Rational; B : Number  ) return Rational;
   function "/" (A : Number;   B : Rational) return Rational;
   function "/" (A : Number;   B : Number)   return Rational;
   
   function ">"  (A : Rational; B : Rational) return Boolean;
   function ">"  (A : Number;   B : Rational) return Boolean;
   function ">"  (A : Rational; B : Number)   return Boolean;

   function "<"  (A : Rational; B : Rational) return Boolean;
   function "<"  (A : Number;   B : Rational) return Boolean;
   function "<"  (A : Rational; B : Number)   return Boolean;

   function ">=" (A : Rational; B : Rational) return Boolean;
   function ">=" (A : Number;   B : Rational) return Boolean;
   function ">=" (A : Rational; B : Number)   return Boolean;

   function "<=" (A : Rational; B : Rational) return Boolean;
   function "<=" (A : Number;   B : Rational) return Boolean;
   function "<=" (A : Rational; B : Number)   return Boolean;

   function "="  (A : Number;   B : Rational) return Boolean;
   function "="  (A : Rational; B : Number)   return Boolean;

   function Numerator   (A : Rational) return Number;
   function Denominator (A : Rational) return Number;
             
   Zero : constant Rational;
   One  : constant Rational;
private
   type Rational is record
      Numerator   : Number;
      Denominator : Number;
   end record;

   Zero : constant Rational := (0, 1);
   One  : constant Rational := (1, 1);
end Generic_Rational;

The package can be instantiated with any integer type. It provides rational numbers represented by a numerator and denominator cleaned from the common divisors. Mixed arithmetic of the base integer type and the rational type is supported. Division to zero raises Constraint_Error. The implementation of the specification above is as follows:

package body Generic_Rational is

   function GCD (A, B : Number) return Number is
   begin
      if A = 0 then
         return B;
      end if;
      if B = 0 then
         return A;
      end if;
      if A > B then
         return GCD (B, A mod B);
      else
         return GCD (A, B mod A);
      end if;
   end GCD;

   function Inverse (A : Rational) return Rational is
   begin
      if A.Numerator > 0 then
         return (A.Denominator, A.Numerator);
      elsif A.Numerator < 0 then
         return (-A.Denominator, -A.Numerator);
      else
         raise Constraint_Error;
      end if;
   end Inverse;

   function "abs" (A : Rational) return Rational is
   begin
      return (abs A.Numerator, A.Denominator);
   end "abs";

   function "+" (A : Rational) return Rational is
   begin
      return A;
   end "+";

   function "-" (A : Rational) return Rational is
   begin
      return (-A.Numerator, A.Denominator);
   end "-";
   
   function "+" (A : Rational; B : Rational) return Rational is
      Common        : constant Number := GCD (A.Denominator, B.Denominator);
      A_Denominator : constant Number := A.Denominator / Common; 
      B_Denominator : constant Number := B.Denominator / Common; 
   begin
      return (A.Numerator * B_Denominator + B.Numerator * A_Denominator) /
             (A_Denominator * B.Denominator);
   end "+";

   function "+" (A : Rational; B : Number) return Rational is
   begin
      return (A.Numerator + B * A.Denominator) / A.Denominator;
   end "+";

   function "+" (A : Number; B : Rational) return Rational is
   begin
      return B + A;
   end "+";

   function "-" (A : Rational; B : Rational) return Rational is
   begin
      return A + (-B);
   end "-";

   function "-" (A : Rational; B : Number) return Rational is
   begin
      return A + (-B);
   end "-";

   function "-" (A : Number; B : Rational) return Rational is
   begin
      return A + (-B);
   end "-";

   function "*" (A : Rational; B : Rational) return Rational is
   begin
      return (A.Numerator * B.Numerator) / (A.Denominator * B.Denominator);
   end "*";

   function "*" (A : Rational; B : Number) return Rational is
      Common : constant Number := GCD (A.Denominator, abs B);
   begin
      return (A.Numerator * B / Common, A.Denominator / Common);
   end "*";

   function "*" (A : Number; B : Rational) return Rational is
   begin
      return B * A;
   end "*";

   function "/" (A : Rational; B : Rational) return Rational is
   begin
      return A * Inverse (B);
   end "/";

   function "/" (A : Rational; B : Number) return Rational is
      Common : constant Number := GCD (abs A.Numerator, abs B);
   begin
      if B > 0 then
         return (A.Numerator / Common, A.Denominator * (B / Common));
      else
         return ((-A.Numerator) / Common, A.Denominator * ((-B) / Common));
      end if;
   end "/";

   function "/" (A : Number; B : Rational) return Rational is
   begin
      return Inverse (B) * A;
   end "/";

   function "/" (A : Number; B : Number) return Rational is
      Common : constant Number := GCD (abs A, abs B);
   begin
      if B = 0 then
         raise Constraint_Error;
      elsif A = 0 then
         return (0, 1);
      elsif A > 0 xor B > 0 then
         return (-(abs A / Common), abs B / Common);
      else
         return (abs A / Common, abs B / Common);
      end if;
   end "/";
   
   function ">" (A, B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator > 0;
   end ">";

   function ">" (A : Number; B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator > 0;
   end ">";

   function ">" (A : Rational; B : Number) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator > 0;
   end ">";

   function "<" (A, B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator < 0;
   end "<";

   function "<" (A : Number; B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator < 0;
   end "<";
   
   function "<" (A : Rational; B : Number) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator < 0;
   end "<";

   function ">=" (A, B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator >= 0;
   end ">=";

   function ">=" (A : Number; B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator >= 0;
   end ">=";

   function ">=" (A : Rational; B : Number) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator >= 0;
   end ">=";

   function "<=" (A, B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator <= 0;
   end "<=";

   function "<=" (A : Number; B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator <= 0;
   end "<=";

   function "<=" (A : Rational; B : Number) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator <= 0;
   end "<=";

   function "=" (A : Number; B : Rational) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator = 0;
   end "=";

   function "=" (A : Rational; B : Number) return Boolean is
      Diff : constant Rational := A - B;
   begin
      return Diff.Numerator = 0;
   end "=";

   function Numerator (A : Rational) return Number is
   begin
      return A.Numerator;
   end Numerator;

   function Denominator (A : Rational) return Number is
   begin
      return A.Denominator;
   end Denominator;

end Generic_Rational;

The implementation uses solution of the greatest common divisor task. Here is the implementation of the test:

with Ada.Numerics.Elementary_Functions;  use Ada.Numerics.Elementary_Functions;
with Ada.Text_IO;                        use Ada.Text_IO;
with Generic_Rational;

procedure Test_Rational is
   package Integer_Rational is new Generic_Rational (Integer);
   use Integer_Rational;
begin
   for Candidate in 2..2**15 loop
      declare
         Sum  : Rational := 1 / Candidate;
      begin
         for Divisor in 2..Integer (Sqrt (Float (Candidate))) loop
            if Candidate mod Divisor = 0 then -- Factor is a divisor of Candidate
               Sum := Sum + One / Divisor + Rational'(Divisor / Candidate);
            end if;
         end loop;
         if Sum = 1 then
            Put_Line (Integer'Image (Candidate) & " is perfect");
         end if;
      end;
   end loop;
end Test_Rational;

The perfect numbers are searched by summing of the reciprocal of each of the divisors of a candidate except 1. This sum must be 1 for a perfect number.

Output:
 6 is perfect
 28 is perfect
 496 is perfect
 8128 is perfect

ALGOL 68

Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
 MODE FRAC = STRUCT( INT num #erator#,  den #ominator#);
 FORMAT frac repr = $g(-0)"//"g(-0)$;
 
 PROC gcd = (INT a, b) INT: # greatest common divisor #
   (a = 0 | b |: b = 0 | a |: ABS a > ABS b  | gcd(b, a MOD b) | gcd(a, b MOD a));
 
 PROC lcm = (INT a, b)INT: # least common multiple #
   a OVER gcd(a, b) * b;
 
 PROC raise not implemented error = ([]STRING args)VOID: (
   put(stand error, ("Not implemented error: ",args, newline));
   stop
 );
 
 PRIO // = 9; # higher then the ** operator #
 OP // = (INT num, den)FRAC: ( # initialise and normalise #
   INT common = gcd(num, den);
   IF den < 0 THEN
     ( -num OVER common, -den OVER common)
   ELSE
     ( num OVER common, den OVER common)
   FI
 );
 
 OP + = (FRAC a, b)FRAC: (
   INT common = lcm(den OF a, den OF b);
   FRAC result := ( common OVER den OF a * num OF a + common OVER den OF b * num OF b, common );
   num OF result//den OF result
 );
 
 OP - = (FRAC a, b)FRAC: a + -b,
    * = (FRAC a, b)FRAC: (
   INT num = num OF a * num OF b,
       den = den OF a * den OF b;
   INT common = gcd(num, den);
   (num OVER common) // (den OVER common)
 );
 
 OP /  = (FRAC a, b)FRAC: a * FRAC(den OF b, num OF b),# real division #
    %  = (FRAC a, b)INT: ENTIER (a / b),               # integer divison #
    %* = (FRAC a, b)FRAC: a/b - FRACINIT ENTIER (a/b), # modulo division #
    ** = (FRAC a, INT exponent)FRAC: 
     IF exponent >= 0 THEN
       (num OF a ** exponent, den OF a ** exponent )
     ELSE
       (den OF a ** exponent, num OF a ** exponent )
     FI;
 
 OP REALINIT = (FRAC frac)REAL: num OF frac / den OF frac,
    FRACINIT = (INT num)FRAC: num // 1,
    FRACINIT = (REAL num)FRAC: (
      # express real number as a fraction # # a future execise! #
      raise not implemented error(("Convert a REAL to a FRAC","!"));
      SKIP
    );
 
 OP <  = (FRAC a, b)BOOL: num OF (a - b) <  0,
    >  = (FRAC a, b)BOOL: num OF (a - b) >  0,
    <= = (FRAC a, b)BOOL: NOT ( a > b ),
    >= = (FRAC a, b)BOOL: NOT ( a < b ),
    =  = (FRAC a, b)BOOL: (num OF a, den OF a) = (num OF b, den OF b),
    /= = (FRAC a, b)BOOL: (num OF a, den OF a) /= (num OF b, den OF b);
 
 # Unary operators #
 OP - = (FRAC frac)FRAC: (-num OF frac, den OF frac),
    ABS = (FRAC frac)FRAC: (ABS num OF frac, ABS den OF frac),
    ENTIER = (FRAC frac)INT: (num OF frac OVER den OF frac) * den OF frac;
 
 COMMENT Operators for extended characters set, and increment/decrement:
 OP +:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a + b ),
    +=: = (FRAC a, REF FRAC b)REF FRAC: ( b := a + b ),
    -:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a - b ),
    *:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a * b ),
    /:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a / b ),
    %:= = (REF FRAC a, FRAC b)REF FRAC: ( a := FRACINIT (a % b) ),
    %*:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a %* b );
 
 # OP aliases for extended character sets (eg: Unicode, APL, ALCOR and GOST 10859) #
 OP ×  = (FRAC a, b)FRAC: a * b,
    ÷  = (FRAC a, b)INT: a OVER b,
    ÷× = (FRAC a, b)FRAC: a MOD b,
    ÷* = (FRAC a, b)FRAC: a MOD b,
    %× = (FRAC a, b)FRAC: a MOD b,
    ≤  = (FRAC a, b)FRAC: a <= b,
    ≥  = (FRAC a, b)FRAC: a >= b,
    ≠  = (FRAC a, b)BOOL: a /= b,
    ↑  = (FRAC frac, INT exponent)FRAC: frac ** exponent,
 
    ÷×:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a MOD b ),
    %×:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a MOD b ),
    ÷*:= = (REF FRAC a, FRAC b)REF FRAC: ( a := a MOD b );
 
 # BOLD aliases for CPU that only support uppercase for 6-bit bytes  - wrist watches #
 OP OVER = (FRAC a, b)INT: a % b,
    MOD = (FRAC a, b)FRAC: a %*b,
    LT = (FRAC a, b)BOOL: a <  b,
    GT = (FRAC a, b)BOOL: a >  b,
    LE = (FRAC a, b)BOOL: a <= b,
    GE = (FRAC a, b)BOOL: a >= b,
    EQ = (FRAC a, b)BOOL: a =  b,
    NE = (FRAC a, b)BOOL: a /= b,
    UP = (FRAC frac, INT exponent)FRAC: frac**exponent;
 
 # the required standard assignment operators #
 OP PLUSAB  = (REF FRAC a, FRAC b)REF FRAC: ( a +:= b ), # PLUS #
    PLUSTO  = (FRAC a, REF FRAC b)REF FRAC: ( a +=: b ), # PRUS #
    MINUSAB = (REF FRAC a, FRAC b)REF FRAC: ( a *:= b ),
    DIVAB   = (REF FRAC a, FRAC b)REF FRAC: ( a /:= b ),
    OVERAB  = (REF FRAC a, FRAC b)REF FRAC: ( a %:= b ),
    MODAB   = (REF FRAC a, FRAC b)REF FRAC: ( a %*:= b );
 
END COMMENT
Example: searching for Perfect Numbers.
 FRAC sum:= FRACINIT 0; 
 FORMAT perfect = $b(" perfect!","")$;
 
 FOR i FROM 2 TO 2**19 DO 
   INT candidate := i;
   FRAC sum := 1 // candidate;
   REAL real sum := 1 / candidate;
   FOR factor FROM 2 TO ENTIER sqrt(candidate) DO
     IF candidate MOD factor = 0 THEN
       sum :=  sum + 1 // factor + 1 // ( candidate OVER factor);
       real sum +:= 1 / factor + 1 / ( candidate OVER factor)
     FI
   OD;
   IF den OF sum  = 1 THEN
     printf(($"Sum of reciprocal factors of "g(-0)" = "g(-0)" exactly, about "g(0,real width) f(perfect)l$, 
             candidate, ENTIER sum, real sum, ENTIER sum = 1))
   FI
 OD
Output:
Sum of reciprocal factors of 6 = 1 exactly, about 1.0000000000000000000000000001 perfect!
Sum of reciprocal factors of 28 = 1 exactly, about 1.0000000000000000000000000001 perfect!
Sum of reciprocal factors of 120 = 2 exactly, about 2.0000000000000000000000000002
Sum of reciprocal factors of 496 = 1 exactly, about 1.0000000000000000000000000001 perfect!
Sum of reciprocal factors of 672 = 2 exactly, about 2.0000000000000000000000000001
Sum of reciprocal factors of 8128 = 1 exactly, about 1.0000000000000000000000000001 perfect!
Sum of reciprocal factors of 30240 = 3 exactly, about 3.0000000000000000000000000002
Sum of reciprocal factors of 32760 = 3 exactly, about 3.0000000000000000000000000003
Sum of reciprocal factors of 523776 = 2 exactly, about 2.0000000000000000000000000005

Arturo

Arturo comes with built-in support for rational numbers.

a: to :rational [1 2]
b: to :rational [3 4]

print ["a:" a]
print ["b:" b]

print ["a + b  :" a + b]
print ["a - b  :" a - b]
print ["a * b  :" a * b]
print ["a / b  :" a / b]
print ["a // b :" a // b]
print ["a % b  :" a % b]

print ["reciprocal b:" reciprocal b]
print ["neg a:" neg a]

print ["pi ~=" to :rational 3.14]
Output:
a: 1/2 
b: 3/4 
a + b  : 5/4 
a - b  : -1/4 
a * b  : 3/8 
a / b  : 2/3  
a // b : 2/3 
a % b  : 1/2 
reciprocal b: 4/3 
neg a: -1/2 
pi ~= 157/50

BBC BASIC

      *FLOAT64
      DIM frac{num, den}
      DIM Sum{} = frac{}, Kf{} = frac{}, One{} = frac{}
      One.num = 1 : One.den = 1
      
      FOR n% = 2 TO 2^19-1
        Sum.num = 1 : Sum.den = n%
        FOR k% = 2 TO SQR(n%)
          IF (n% MOD k%) = 0 THEN
            Kf.num = 1 : Kf.den = k%
            PROCadd(Sum{}, Kf{})
            PROCnormalise(Sum{})
            Kf.den = n% DIV k%
            PROCadd(Sum{}, Kf{})
            PROCnormalise(Sum{})
          ENDIF
        NEXT
        IF FNeq(Sum{}, One{}) PRINT n% " is perfect"
      NEXT n%
      END
      
      DEF PROCabs(a{}) : a.num = ABS(a.num) : ENDPROC
      DEF PROCneg(a{}) : a.num = -a.num : ENDPROC
      
      DEF PROCadd(a{}, b{})
      LOCAL t : t = a.den * b.den
      a.num = a.num * b.den + b.num * a.den
      a.den = t
      ENDPROC
      
      DEF PROCsub(a{}, b{})
      LOCAL t : t = a.den * b.den
      a.num = a.num * b.den - b.num * a.den
      a.den = t
      ENDPROC
      
      DEF PROCmul(a{}, b{})
      a.num *= b.num : a.den *= b.den
      ENDPROC
      
      DEF PROCdiv(a{}, b{})
      a.num *= b.den : a.den *= b.num
      ENDPROC
      
      DEF FNeq(a{}, b{}) = a.num * b.den = b.num * a.den
      DEF FNlt(a{}, b{}) = a.num * b.den < b.num * a.den
      DEF FNgt(a{}, b{}) = a.num * b.den > b.num * a.den
      DEF FNne(a{}, b{}) = a.num * b.den <> b.num * a.den
      DEF FNle(a{}, b{}) = a.num * b.den <= b.num * a.den
      DEF FNge(a{}, b{}) = a.num * b.den >= b.num * a.den
      
      DEF PROCnormalise(a{})
      LOCAL a, b, t
      a = a.num : b = a.den
      WHILE b <> 0
        t = a
        a = b
        b = t - b * INT(t / b)
      ENDWHILE
      a.num /= a : a.den /= a
      IF a.den < 0 a.num *= -1 : a.den *= -1
      ENDPROC

Output:

         6 is perfect
        28 is perfect
       496 is perfect
      8128 is perfect

C

C does not have overloadable operators. The following implementation does not define all operations so as to keep the example short. Note that the code passes around struct values instead of pointers to keep it simple, a practice normally avoided for efficiency reasons.

#include <stdio.h>
#include <stdlib.h>
#define FMT "%lld"
typedef long long int fr_int_t;
typedef struct { fr_int_t num, den; } frac;

fr_int_t gcd(fr_int_t m, fr_int_t n)
{
	fr_int_t t;
	while (n) { t = n; n = m % n; m = t; }
	return m;
}

frac frac_new(fr_int_t num, fr_int_t den)
{
	frac a;
	if (!den) {
		printf("divide by zero: "FMT"/"FMT"\n", num, den);
		abort();
	}

	int g = gcd(num, den);

	if (g)	{ num /= g; den /= g; }
	else	{ num = 0; den = 1;   }

	if (den < 0) {
		den = -den;
		num = -num;
	}
	a.num = num; a.den = den;
	return a;
}

#define BINOP(op, n, d) frac frac_##op(frac a, frac b) { return frac_new(n,d); }
BINOP(add, a.num * b.den + b.num * a.den, a.den * b.den);
BINOP(sub, a.num * b.den - b.num + a.den, a.den * b.den);
BINOP(mul, a.num * b.num, a.den * b.den);
BINOP(div, a.num * b.den, a.den * b.num);

int frac_cmp(frac a, frac b) {
	int l = a.num * b.den, r = a.den * b.num;
	return l < r ? -1 : l > r;
}
#define frac_cmp_int(a, b) frac_cmp(a, frac_new(b, 1))
int frtoi(frac a) { return a.den / a.num; }
double frtod(frac a) { return (double)a.den / a.num; }

int main()
{
	int n, k;
	frac sum, kf;

	for (n = 2; n < 1<<19; n++) {
		sum = frac_new(1, n);

		for (k = 2; k * k < n; k++) {
			if (n % k) continue;
			kf = frac_new(1, k);
			sum = frac_add(sum, kf);

			kf = frac_new(1, n / k);
			sum = frac_add(sum, kf);
		}
		if (frac_cmp_int(sum, 1) == 0) printf("%d\n", n);
	}

	return 0;
}

See Rational Arithmetic/C

C#

[This section is included from a subpage and should be edited there, not here.]
using System;

struct Fraction : IEquatable<Fraction>, IComparable<Fraction>
{
    public readonly long Num;
    public readonly long Denom;

    public Fraction(long num, long denom)
    {
        if (num == 0)
        {
            denom = 1;
        }
        else if (denom == 0)
        {
            throw new ArgumentException("Denominator may not be zero", "denom");
        }
        else if (denom < 0)
        {
            num = -num;
            denom = -denom;
        }

        long d = GCD(num, denom);
        this.Num = num / d;
        this.Denom = denom / d;
    }

    private static long GCD(long x, long y)
    {
        return y == 0 ? x : GCD(y, x % y);
    }

    private static long LCM(long x, long y)
    {
        return x / GCD(x, y) * y;
    }

    public Fraction Abs()
    {
        return new Fraction(Math.Abs(Num), Denom);
    }

    public Fraction Reciprocal()
    {
        return new Fraction(Denom, Num);
    }

    #region Conversion Operators

    public static implicit operator Fraction(long i)
    {
        return new Fraction(i, 1);
    }

    public static explicit operator double(Fraction f)
    {
        return f.Num == 0 ? 0 : (double)f.Num / f.Denom;
    }

    #endregion

    #region Arithmetic Operators

    public static Fraction operator -(Fraction f)
    {
        return new Fraction(-f.Num, f.Denom);
    }

    public static Fraction operator +(Fraction a, Fraction b)
    {
        long m = LCM(a.Denom, b.Denom);
        long na = a.Num * m / a.Denom;
        long nb = b.Num * m / b.Denom;
        return new Fraction(na + nb, m);
    }

    public static Fraction operator -(Fraction a, Fraction b)
    {
        return a + (-b);
    }

    public static Fraction operator *(Fraction a, Fraction b)
    {
        return new Fraction(a.Num * b.Num, a.Denom * b.Denom);
    }

    public static Fraction operator /(Fraction a, Fraction b)
    {
        return a * b.Reciprocal();
    }

    public static Fraction operator %(Fraction a, Fraction b)
    {
        long l = a.Num * b.Denom, r = a.Denom * b.Num;
        long n = l / r;
        return new Fraction(l - n * r, a.Denom * b.Denom);
    }

    #endregion

    #region Comparison Operators

    public static bool operator ==(Fraction a, Fraction b)
    {
        return a.Num == b.Num && a.Denom == b.Denom;
    }

    public static bool operator !=(Fraction a, Fraction b)
    {
        return a.Num != b.Num || a.Denom != b.Denom;
    }

    public static bool operator <(Fraction a, Fraction b)
    {
        return (a.Num * b.Denom) < (a.Denom * b.Num);
    }

    public static bool operator >(Fraction a, Fraction b)
    {
        return (a.Num * b.Denom) > (a.Denom * b.Num);
    }

    public static bool operator <=(Fraction a, Fraction b)
    {
        return !(a > b);
    }

    public static bool operator >=(Fraction a, Fraction b)
    {
        return !(a < b);
    }

    #endregion

    #region Object Members

    public override bool Equals(object obj)
    {
        if (obj is Fraction)
            return ((Fraction)obj) == this;
        else
            return false;
    }

    public override int GetHashCode()
    {
        return Num.GetHashCode() ^ Denom.GetHashCode();
    }

    public override string ToString()
    {
        return Num.ToString() + "/" + Denom.ToString();
    }

    #endregion

    #region IEquatable<Fraction> Members

    public bool Equals(Fraction other)
    {
        return other == this;
    }

    #endregion

    #region IComparable<Fraction> Members

    public int CompareTo(Fraction other)
    {
        return (this.Num * other.Denom).CompareTo(this.Denom * other.Num);
    }

    #endregion
}

Test program:

using System;

static class Program
{
    static void Main(string[] args)
    {
        int max = 1 << 19;
        for (int candidate = 2; candidate < max; candidate++)
        {
            Fraction sum = new Fraction(1, candidate);
            int max2 = (int)Math.Sqrt(candidate);
            for (int factor = 2; factor <= max2; factor++)
            {
                if (candidate % factor == 0)
                {
                    sum += new Fraction(1, factor);
                    sum += new Fraction(1, candidate / factor);
                }
            }

            if (sum == 1)
                Console.WriteLine("{0} is perfect", candidate);
        }
    }
}
Output:
6 is perfect
28 is perfect
496 is perfect
8128 is perfect

C++

Library: Boost

Boost provides a rational number template.

#include <iostream>
#include "math.h"
#include "boost/rational.hpp"

typedef  boost::rational<int> frac;

bool is_perfect(int c)
{
    frac sum(1, c);
    for (int f = 2;f < sqrt(static_cast<float>(c)); ++f){

        if (c % f == 0) sum += frac(1,f) + frac(1, c/f);
    }
    if (sum.denominator() == 1){
 	return (sum == 1);
    }
    return false;
}

int main()
{
    for (int candidate = 2; candidate < 0x80000; ++candidate){
        if (is_perfect(candidate)) 
	        std::cout << candidate << " is perfect" << std::endl;
    }
    return 0;
}

Without using external libraries

#include <cmath>
#include <cstdint>
#include <iostream>
#include <numeric>
#include <stdexcept>

class Rational {
public:
	/// Constructors ///
	Rational() : numer(0), denom(1) {}

	Rational(const int64_t number) : numer(number), denom(1) {}

	Rational(const int64_t& numerator, const int64_t& denominator) : numer(numerator), denom(denominator) {
		if ( numer == 0 ) {
			denom = 1;
		} else if ( denom == 0 ) {
		    throw std::invalid_argument("Denominator cannot be zero: " + denom);
		} else if ( denom < 0 ) {
			numer = -numer;
			denom = -denom;
		}

		int64_t divisor = std::gcd(numerator, denom);
		numer = numer / divisor;
		denom = denom / divisor;
	}

	Rational(const Rational& other) : numer(other.numer), denom(other.denom) {}

	/// Operators ///
	Rational& operator=(const Rational& other) {
	    if ( *this != other ) { numer = other.numer; denom = other.denom; }
	    return *this;
	}

	bool operator!=(const Rational& other) const { return ! ( *this == other ); }

	bool operator==(const Rational& other) const {
	    if ( numer == other.numer && denom == other.denom ) { return true; }
	    return false;
	}

	Rational& operator+=(const Rational& other) {
	    *this = Rational(numer* other.denom + other.numer * denom, denom * other.denom);
	    return *this;
	}

	Rational operator+(const Rational& other) const { return Rational(*this) += other; }

	Rational& operator-=(const Rational& other) {
	    Rational temp(other);
	    temp.numer = -temp.numer;
	    return *this += temp;
	}
	Rational operator-(const Rational& other) const { return Rational(*this) -= other; }

	Rational& operator*=(const Rational& other) {
	    *this = Rational(numer * other.numer, denom * other.denom);
	    return *this;
	}
	
	Rational operator*(const Rational& other) const { return Rational(*this) *= other; };

	Rational& operator/=(const Rational other) {
	    Rational temp(other.denom, other.numer);
	    *this *= temp;
	    return *this;
	}

	Rational operator/(const Rational& other) const { return Rational(*this) /= other; };

	bool operator<(const Rational& other) const { return numer * other.denom < denom * other.numer; }

	bool operator<=(const Rational& other) const { return ! ( other < *this ); }

	bool operator>(const Rational& other) const { return other < *this; }

	bool operator>=(const Rational& other) const { return ! ( *this < other ); }

	Rational operator-() const { return Rational(-numer, denom); }

	Rational& operator++() { numer += denom; return *this; }

	Rational operator++(int) { Rational temp = *this; ++*this; return temp; }

	Rational& operator--() { numer -= denom; return *this; }

	Rational operator--(int) { Rational temp = *this; --*this; return temp; }

	friend std::ostream& operator<<(std::ostream& outStream, const Rational& other) {
		outStream << other.numer << "/" << other.denom;
		return outStream;
	}

	/// Methods ///
	Rational reciprocal() const { return Rational(denom, numer); }

	Rational positive() const { return Rational(abs(numer), denom); }

	int64_t to_integer() const { return numer / denom; }

	double to_double() const { return (double) numer / denom; }

	int64_t hash() const { return std::hash<int64_t>{}(numer) ^ std::hash<int64_t>{}(denom); }
	
private:
	int64_t numer;
	int64_t denom;
};

int main() {
	std::cout << "Perfect numbers less than 2^19:" << std::endl;
	const int32_t limit = 1 << 19;
	for ( int32_t candidate = 2; candidate < limit; ++candidate ) {
		Rational sum = Rational(1, candidate);
		int32_t square_root = (int32_t) sqrt(candidate);
		for ( int32_t factor = 2; factor <= square_root; ++factor ) {
			if ( candidate % factor == 0 ) {
				sum += Rational(1, factor);
				sum += Rational(1, candidate / factor);
			}
		}

		if ( sum == Rational(1) ) {
			std::cout << candidate << std::endl;
		}
	}
}
Output:
Perfect numbers less than 2^19:
6
28
496
8128

Clojure

Ratios are built in to Clojure and support math operations already. They automatically reduce and become Integers if possible.

user> 22/7
22/7
user> 34/2
17
user> (+ 37/5 42/9)
181/15

Common Lisp

Common Lisp has rational numbers built-in and integrated with all other number types. Common Lisp's number system is not extensible so reimplementing rational arithmetic would require all-new operator names.

(loop for candidate from 2 below (expt 2 19)
      for sum = (+ (/ candidate)
                   (loop for factor from 2 to (isqrt candidate)
                         when (zerop (mod candidate factor))
                           sum (+ (/ factor) (/ (floor candidate factor)))))
      when (= sum 1)
        collect candidate)

D

import std.bigint, std.traits, std.conv;

// std.numeric.gcd doesn't work with BigInt.
T gcd(T)(in T a, in T b) pure nothrow {
    return (b != 0) ? gcd(b, a % b) : (a < 0) ? -a : a;
}

T lcm(T)(in T a, in T b) pure nothrow {
    return a / gcd(a, b) * b;
}

struct RationalT(T) if (!isUnsigned!T) {
    private T num, den; // Numerator & denominator.

    private enum Type { NegINF = -2,
                        NegDEN = -1,
                        NaRAT  =  0,
                        NORMAL =  1,
                        PosINF =  2 };

    this(U : RationalT)(U n) pure nothrow {
        num = n.num;
        den = n.den;
    }

    this(U)(in U n) pure nothrow if (isIntegral!U) {
        num = toT(n);
        den = 1UL;
    }

    this(U, V)(in U n, in V d) pure nothrow {
        num = toT(n);
        den = toT(d);
        const common = gcd(num, den);
        if (common != 0) {
            num /= common;
            den /= common;
        } else { // infinite or NOT a Number
            num = (num == 0) ? 0 : (num < 0) ? -1 : 1;
            den = 0;
        }
        if (den < 0) { // Assure den is non-negative.
            num = -num;
            den = -den;
        }
    }

    static T toT(U)(in ref U n) pure nothrow if (is(U == T)) {
        return n;
    }

    static T toT(U)(in ref U n) pure nothrow if (!is(U == T)) {
        T result = n;
        return result;
    }

    T numerator() const pure nothrow @property {
        return num;
    }

    T denominator() const pure nothrow @property {
        return den;
    }

    string toString() const /*pure nothrow*/ {
        if (den != 0)
            return num.text ~ (den == 1 ? "" : "/" ~ den.text);
        if (num == 0)
            return "NaRat";
        else
            return ((num < 0) ? "-" : "+") ~ "infRat";
    }

    real toReal() pure const nothrow {
        static if (is(T == BigInt))
            return num.toLong / real(den.toLong);
        else
            return num / real(den);
    }

    RationalT opBinary(string op)(in RationalT r)
    const pure nothrow if (op == "+" || op == "-") {
        T common = lcm(den, r.den);
        T n = mixin("common / den * num" ~ op ~
                    "common / r.den * r.num" );
        return RationalT(n, common);
    }

    RationalT opBinary(string op)(in RationalT r)
    const pure nothrow if (op == "*") {
        return RationalT(num * r.num, den * r.den);
    }

    RationalT opBinary(string op)(in RationalT r)
    const pure nothrow if (op == "/") {
        return RationalT(num * r.den, den * r.num);
    }

    RationalT opBinary(string op, U)(in U r)
    const pure nothrow if (isIntegral!U && (op == "+" ||
                           op == "-" || op == "*" || op == "/")) {
        return opBinary!op(RationalT(r));
    }

    RationalT opBinary(string op)(in size_t p)
    const pure nothrow if (op == "^^") {
        return RationalT(num ^^ p, den ^^ p);
    }

    RationalT opBinaryRight(string op, U)(in U l)
    const pure nothrow if (isIntegral!U) {
        return RationalT(l).opBinary!op(RationalT(num, den));
    }

    RationalT opOpAssign(string op, U)(in U l) pure /*nothrow*/ {
        mixin("this = this " ~ op ~ "l;");
        return this;
    }

    RationalT opUnary(string op)()
    const pure nothrow if (op == "+" || op == "-") {
        return RationalT(mixin(op ~ "num"), den);
    }

    bool opCast(U)() const if (is(U == bool)) {
        return num != 0;
    }

    bool opEquals(U)(in U r) const pure nothrow {
        RationalT rhs = RationalT(r);
        if (type() == Type.NaRAT || rhs.type() == Type.NaRAT)
            return false;
        return num == rhs.num && den == rhs.den;
    }

    int opCmp(U)(in U r) const pure nothrow {
        auto rhs = RationalT(r);
        if (type() == Type.NaRAT || rhs.type() == Type.NaRAT)
            throw new Error("Compare involve a NaRAT.");
        if (type() != Type.NORMAL ||
            rhs.type() != Type.NORMAL) // for infinite
            return (type() == rhs.type()) ? 0 :
                ((type() < rhs.type()) ? -1 : 1);
        auto diff = num * rhs.den - den * rhs.num;
        return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1);
    }

    Type type() const pure nothrow {
        if (den > 0) return Type.NORMAL;
        if (den < 0) return Type.NegDEN;
        if (num > 0) return Type.PosINF;
        if (num < 0) return Type.NegINF;
        return Type.NaRAT;
    }
}

RationalT!U rational(U)(in U n) pure nothrow {
    return typeof(return)(n);
}

RationalT!(CommonType!(U1, U2))
rational(U1, U2)(in U1 n, in U2 d) pure nothrow {
    return typeof(return)(n, d);
}

alias Rational = RationalT!BigInt;

version (arithmetic_rational_main) { // Test.
    void main() {
        import std.stdio, std.math;
        alias RatL = RationalT!long;

        foreach (immutable p; 2 .. 2 ^^ 19) {
            auto sum = RatL(1, p);
            immutable limit = 1 + cast(uint)real(p).sqrt;
            foreach (immutable factor; 2 .. limit)
                if (p % factor == 0)
                    sum += RatL(1, factor) + RatL(factor, p);
            if (sum.denominator == 1)
                writefln("Sum of recipr. factors of %6s = %s exactly%s",
                         p, sum, (sum == 1) ? ", perfect." : ".");
        }
    }
}

Use the -version=rational_arithmetic_main compiler switch to run the test code.

Output:
Sum of recipr. factors of      6 = 1 exactly, perfect.
Sum of recipr. factors of     28 = 1 exactly, perfect.
Sum of recipr. factors of    120 = 2 exactly.
Sum of recipr. factors of    496 = 1 exactly, perfect.
Sum of recipr. factors of    672 = 2 exactly.
Sum of recipr. factors of   8128 = 1 exactly, perfect.
Sum of recipr. factors of  30240 = 3 exactly.
Sum of recipr. factors of  32760 = 3 exactly.
Sum of recipr. factors of 523776 = 2 exactly.

Currently RationalT!BigInt is not fast.

Delphi

[[1]]
Translation of: C#
program Arithmetic_Rational;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Boost.Rational;

var
  sum: TFraction;
  max: Integer = 1 shl 19;
  candidate, max2, factor: Integer;

begin
  for candidate := 2 to max - 1 do
  begin
    sum := Fraction(1, candidate);
    max2 := Trunc(Sqrt(candidate));
    for factor := 2 to max2 do
    begin
      if (candidate mod factor) = 0 then
      begin
        sum := sum + Fraction(1, factor);
        sum := sum + Fraction(1, candidate div factor);
      end;
    end;
    if sum = Fraction(1) then
      Writeln(candidate, ' is perfect');
  end;
  Readln;
end.
Output:
6 is perfect
28 is perfect
496 is perfect
8128 is perfect

EchoLisp

EchoLisp supports rational numbers as native type. "Big" rational i.e bigint/bigint are not supported.

;; Finding perfect numbers
(define (sum/inv n) ;; look for div's in [2..sqrt(n)] and add 1/n
	(for/fold (acc (/ n)) [(i (in-range 2 (sqrt n)))]
		#:break (> acc 1) ; no hope
		(when (zero? (modulo n i )) 
			(set! acc (+ acc (/ i) (/ i n))))))
Output:
;; rational operations
(+ 1/42 1/666)  59/2331
42/666  7/111
(expt 3/4 7)  2187/16384 ; 3/4 ^7
(/ 6 8)  3/4   ;; / operator → rational
(// 6 8)  0.75 ;; // operator → float
(* 6/7 14/12)  1

;; even perfect numbers (up to 100000)
(for [(i (in-range 4 100000 2))] ;; 8 seconds
	(when (= (sum/inv i) 1) 
		(printf "🍏 🍒 🍓 %d is perfect." i)))

🍏 🍒 🍓 6 is perfect.
🍏 🍒 🍓 28 is perfect.
🍏 🍒 🍓 496 is perfect.
🍏 🍒 🍓 8128 is perfect.

Elisa

component RationalNumbers;
  type Rational;
       Rational(Numerator = integer, Denominater = integer) -> Rational;

       Rational + Rational -> Rational; 
       Rational - Rational -> Rational;
       Rational * Rational -> Rational; 
       Rational / Rational -> Rational;
 
       Rational == Rational -> boolean; 
       Rational <> Rational -> boolean; 
       Rational >= Rational -> boolean; 
       Rational <= Rational -> boolean;
       Rational >  Rational -> boolean; 
       Rational <  Rational -> boolean;
 
       + Rational -> Rational;
       - Rational -> Rational;
       abs(Rational) -> Rational;
      
       Rational(integer) -> Rational;
       Numerator(Rational) -> integer;
       Denominator(Rational) -> integer;
  begin
       Rational(A,B) = Rational:[A;B];

       R1 + R2 = Normalize( R1.A * R2.B + R1.B * R2.A, R1.B * R2.B);
       R1 - R2 = Normalize( R1.A * R2.B - R1.B * R2.A, R1.B * R2.B);
       R1 * R2 = Normalize( R1.A * R2.A, R1.B * R2.B);
       R1 / R2 = Normalize( R1.A * R2.B, R1.B * R2.A);

       R1 == R2 = [ R = (R1 - R2); R.A == 0]; 
       R1 <> R2 = [ R = (R1 - R2); R.A <> 0];
       R1 >= R2 = [ R = (R1 - R2); R.A >= 0];
       R1 <= R2 = [ R = (R1 - R2); R.A <= 0];
       R1 > R2  = [ R = (R1 - R2); R.A > 0];
       R1 < R2  = [ R = (R1 - R2); R.A < 0];

       + R = R;
       - R = Rational(-R.A, R.B);

       abs(R) = Rational(abs(R.A), abs(R.B)); 
       Rational(I) = Rational (I, 1);
       Numerator(R) = R.A;
       Denominator(R) = R.B;

				<< internal definitions >>

       Normalize (A = integer, B = integer) -> Rational;
       Normalize (A, B) = [ exception( B == 0, "Illegal Rational Number");
	                    Common = GCD(abs(A), abs(B)); 
			    if B < 0 then Rational(-A / Common, -B / Common)
			             else Rational( A / Common,  B / Common) ];

       GCD (A = integer, B = integer) -> integer;
       GCD (A, B) = [ if A == 0 then return(B);   
	              if B == 0 then return(A); 
		      if A > B  then GCD (B, mod(A,B))
 		                else GCD (A, mod(B,A)) ]; 

end component RationalNumbers;

Tests

use RationalNumbers;

PerfectNumbers( Limit = integer) -> multi(integer);
PerfectNumbers( Limit) =
  	      [ Candidate = 2 .. Limit; 
		Sum:= Rational(1,Candidate);
		[ Divisor = 2 .. integer(sqrt(real(Candidate)));
		  if mod(Candidate, Divisor) == 0 then 
			Sum := Sum + Rational(1, Divisor) + Rational(Divisor, Candidate);
		];
		if Sum == Rational(1,1) then Candidate
              ];

PerfectNumbers(10000)?
Output:
6
28
496
8128

Elixir

defmodule Rational do
  import Kernel, except: [div: 2]
  
  defstruct numerator: 0, denominator: 1
  
  def new(numerator), do: %Rational{numerator: numerator, denominator: 1}
  
  def new(numerator, denominator) do
    sign = if numerator * denominator < 0, do: -1, else: 1
    {numerator, denominator} = {abs(numerator), abs(denominator)}
    gcd = gcd(numerator, denominator)
    %Rational{numerator: sign * Kernel.div(numerator, gcd),
              denominator: Kernel.div(denominator, gcd)}
  end
  
  def add(a, b) do
    {a, b} = convert(a, b)
    new(a.numerator * b.denominator + b.numerator * a.denominator,
        a.denominator * b.denominator)
  end
  
  def sub(a, b) do
    {a, b} = convert(a, b)
    new(a.numerator * b.denominator - b.numerator * a.denominator,
        a.denominator * b.denominator)
  end
  
  def mult(a, b) do
    {a, b} = convert(a, b)
    new(a.numerator * b.numerator, a.denominator * b.denominator)
  end
  
  def div(a, b) do
    {a, b} = convert(a, b)
    new(a.numerator * b.denominator, a.denominator * b.numerator)
  end
  
  defp convert(a), do: if is_integer(a), do: new(a), else: a
  
  defp convert(a, b), do: {convert(a), convert(b)}
  
  defp gcd(a, 0), do: a
  defp gcd(a, b), do: gcd(b, rem(a, b))
end

defimpl Inspect, for: Rational do
  def inspect(r, _opts) do
    "%Rational<#{r.numerator}/#{r.denominator}>"
  end
end

Enum.each(2..trunc(:math.pow(2,19)), fn candidate ->
  sum = 2 .. round(:math.sqrt(candidate))
        |> Enum.reduce(Rational.new(1, candidate), fn factor,sum ->
             if rem(candidate, factor) == 0 do
               Rational.add(sum, Rational.new(1, factor))
               |> Rational.add(Rational.new(1, div(candidate, factor)))
             else
               sum
             end
           end)
  if sum.denominator == 1 do
    :io.format "Sum of recipr. factors of ~6w = ~w exactly ~s~n",
           [candidate, sum.numerator, (if sum.numerator == 1, do: "perfect!", else: "")]
  end
end)
Output:
Sum of recipr. factors of      6 = 1 exactly perfect!
Sum of recipr. factors of     28 = 1 exactly perfect!
Sum of recipr. factors of    120 = 2 exactly
Sum of recipr. factors of    496 = 1 exactly perfect!
Sum of recipr. factors of    672 = 2 exactly
Sum of recipr. factors of   8128 = 1 exactly perfect!
Sum of recipr. factors of  30240 = 3 exactly
Sum of recipr. factors of  32760 = 3 exactly
Sum of recipr. factors of 523776 = 2 exactly

ERRE

PROGRAM RATIONAL_ARITH

!
! for rosettacode.org
!

TYPE RATIONAL=(NUM,DEN)

DIM SUM:RATIONAL,ONE:RATIONAL,KF:RATIONAL

DIM A:RATIONAL,B:RATIONAL
PROCEDURE ABS(A.->A.)
      A.NUM=ABS(A.NUM)
END PROCEDURE

PROCEDURE NEG(A.->A.)
      A.NUM=-A.NUM
END PROCEDURE

PROCEDURE ADD(A.,B.->A.)
      LOCAL T
      T=A.DEN*B.DEN
      A.NUM=A.NUM*B.DEN+B.NUM*A.DEN
      A.DEN=T
END PROCEDURE

PROCEDURE SUB(A.,B.->A.)
      LOCAL T
      T=A.DEN*B.DEN
      A.NUM=A.NUM*B.DEN-B.NUM*A.DEN
      A.DEN=T
END PROCEDURE

PROCEDURE MULT(A.,B.->A.)
      A.NUM*=B.NUM  A.DEN*=B.DEN
END PROCEDURE

PROCEDURE DIVIDE(A.,B.->A.)
      A.NUM*=B.DEN
      A.DEN*=B.NUM
END PROCEDURE

PROCEDURE EQ(A.,B.->RES%)
      RES%=A.NUM*B.DEN=B.NUM*A.DEN
END PROCEDURE

PROCEDURE LT(A.,B.->RES%)
      RES%=A.NUM*B.DEN<B.NUM*A.DEN
END PROCEDURE

PROCEDURE GT(A.,B.->RES%)
      RES%=A.NUM*B.DEN>B.NUM*A.DEN
END PROCEDURE

PROCEDURE NE(A.,B.->RES%)
      RES%=A.NUM*B.DEN<>B.NUM*A.DEN
END PROCEDURE

PROCEDURE LE(A.,B.->RES%)
      RES%=A.NUM*B.DEN<=B.NUM*A.DEN
END PROCEDURE

PROCEDURE GE(A.,B.->RES%)
      RES%=A.NUM*B.DEN>=B.NUM*A.DEN
END PROCEDURE

PROCEDURE NORMALIZE(A.->A.)
      LOCAL A,B,T
      A=A.NUM   B=A.DEN
      WHILE B<>0 DO
        T=A
        A=B
        B=T-B*INT(T/B)
      END WHILE
      A.NUM/=A  A.DEN/=A
      IF A.DEN<0 THEN A.NUM*=-1 A.DEN*=-1 END IF
END PROCEDURE

BEGIN
    ONE.NUM=1 ONE.DEN=1
    FOR N=2 TO 2^19-1 DO
      SUM.NUM=1 SUM.DEN=N
      FOR K=2 TO SQR(N) DO
        IF N=K*INT(N/K) THEN
          KF.NUM=1 KF.DEN=K
          ADD(SUM.,KF.->SUM.)
          NORMALIZE(SUM.->SUM.)
          KF.DEN=INT(N/K)
          ADD(SUM.,KF.->SUM.)
          NORMALIZE(SUM.->SUM.)
        END IF
      END FOR
      EQ(SUM.,ONE.->RES%)
      IF RES% THEN PRINT(N;" is perfect") END IF
   END FOR
END PROGRAM
Output:
 6  is perfect
 28  is perfect
 496  is perfect
 8128  is perfect

F#

The F# Powerpack library defines the BigRational data type.

type frac = Microsoft.FSharp.Math.BigRational

let perf n = 1N = List.fold (+) 0N (List.map (fun i -> if n % i = 0 then 1N/frac.FromInt(i) else 0N) [2..n])

for i in 1..(1<<<19) do if (perf i) then printfn "%i is perfect" i

Factor

ratio is a built-in numeric type.

USING: generalizations io kernel math math.functions
math.primes.factors math.ranges prettyprint sequences ;
IN: rosetta-code.arithmetic-rational

2/5              ! literal syntax 2/5
2/4              ! automatically simplifies to 1/2
5/1              ! automatically coerced to 5
26/5             ! mixed fraction 5+1/5
13/178 >fraction ! get the numerator and denominator 13 178
8 recip          ! get the reciprocal 1/8

! ratios can be any size
12417829731289312/61237812937138912735712
8 ndrop ! clear the stack
! arithmetic works the same as any other number.

: perfect? ( n -- ? )
    divisors rest [ recip ] map-sum 1 = ;

"Perfect numbers <= 2^19: " print
2 19 ^ [1,b] [ perfect? ] filter .
Output:
Perfect numbers <= 2^19:
V{ 6 28 496 8128 }

Fermat

Fermat supports rational aritmetic natively.

for n=2 to 2^19 by 2 do
    s:=3/n;
    m:=1;
    while m<=n/3 do
        if Divides(m,n) then s:=s+1/m; fi;
        m:=m+1;
    od;
    if s=2 then !!n fi;
od;
Output:
6
28
496
8128

Forth

\ Rationals can use any double cell operations:  2!, 2@, 2dup, 2swap, etc.
\ Uses the stack convention of the built-in "*/" for int * frac -> int

: numerator  drop ;
: denominator nip ;

: s>rat      1 ;		\ integer to rational  (n/1)
: rat>s      / ;		\ integer
: rat>frac   mod ;		\ fractional part
: rat>float  swap s>f s>f f/ ;

: rat.  swap 1 .r [char] / emit . ;

\ normalize: factors out gcd and puts sign into numerator
: gcd ( a b -- gcd ) begin ?dup while tuck mod repeat ;
: rat-normalize ( rat -- rat ) 2dup gcd tuck / >r / r> ;

: rat-abs     swap abs    swap ;
: rat-negate  swap negate swap ;
: 1/rat       over 0< if negate swap negate else swap then ;

: rat+ ( a b c d -- ad+bc bd )
  rot 2dup * >r
   rot * >r * r> +
  r> rat-normalize ;
: rat-  rat-negate rat+ ;

: rat* ( a b c d -- ac bd )
  rot * >r * r> rat-normalize ;
: rat/  swap rat* ;

: rat-equal  d= ;
: rat-less ( a b c d -- ad<bc )
  -rot * >r * r> < ;
: rat-more  2swap rat-less ;

: rat-inc  tuck + swap ;
: rat-dec  tuck - swap ;

Fortran

Works with: Fortran version 90 and later
module module_rational

  implicit none
  private
  public :: rational
  public :: rational_simplify
  public :: assignment (=)
  public :: operator (//)
  public :: operator (+)
  public :: operator (-)
  public :: operator (*)
  public :: operator (/)
  public :: operator (<)
  public :: operator (<=)
  public :: operator (>)
  public :: operator (>=)
  public :: operator (==)
  public :: operator (/=)
  public :: abs
  public :: int
  public :: modulo
  type rational
    integer :: numerator
    integer :: denominator
  end type rational
  interface assignment (=)
    module procedure assign_rational_int, assign_rational_real
  end interface
  interface operator (//)
    module procedure make_rational
  end interface
  interface operator (+)
    module procedure rational_add
  end interface
  interface operator (-)
    module procedure rational_minus, rational_subtract
  end interface
  interface operator (*)
    module procedure rational_multiply
  end interface
  interface operator (/)
    module procedure rational_divide
  end interface
  interface operator (<)
    module procedure rational_lt
  end interface
  interface operator (<=)
    module procedure rational_le
  end interface
  interface operator (>)
    module procedure rational_gt
  end interface
  interface operator (>=)
    module procedure rational_ge
  end interface
  interface operator (==)
    module procedure rational_eq
  end interface
  interface operator (/=)
    module procedure rational_ne
  end interface
  interface abs
    module procedure rational_abs
  end interface
  interface int
    module procedure rational_int
  end interface
  interface modulo
    module procedure rational_modulo
  end interface

contains

  recursive function gcd (i, j) result (res)
    integer, intent (in) :: i
    integer, intent (in) :: j
    integer :: res
    if (j == 0) then
      res = i
    else
      res = gcd (j, modulo (i, j))
    end if
  end function gcd

  function rational_simplify (r) result (res)
    type (rational), intent (in) :: r
    type (rational) :: res
    integer :: g
    g = gcd (r % numerator, r % denominator)
    res = r % numerator / g // r % denominator / g
  end function rational_simplify

  function make_rational (numerator, denominator) result (res)
    integer, intent (in) :: numerator
    integer, intent (in) :: denominator
    type (rational) :: res
    res = rational (numerator, denominator)
  end function make_rational

  subroutine assign_rational_int (res, i)
    type (rational), intent (out), volatile :: res
    integer, intent (in) :: i
    res = i // 1
  end subroutine assign_rational_int

  subroutine assign_rational_real (res, x)
    type (rational), intent(out), volatile :: res
    real, intent (in) :: x
    integer :: x_floor
    real :: x_frac
    x_floor = floor (x)
    x_frac = x - x_floor
    if (x_frac == 0) then
      res = x_floor // 1
    else
      res = (x_floor // 1) + (1 // floor (1 / x_frac))
    end if
  end subroutine assign_rational_real

  function rational_add (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: res
    res = r % numerator * s % denominator + r % denominator * s % numerator // &
        & r % denominator * s % denominator
  end function rational_add

  function rational_minus (r) result (res)
    type (rational), intent (in) :: r
    type (rational) :: res
    res = - r % numerator // r % denominator
  end function rational_minus

  function rational_subtract (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: res
    res = r % numerator * s % denominator - r % denominator * s % numerator // &
        & r % denominator * s % denominator
  end function rational_subtract

  function rational_multiply (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: res
    res = r % numerator * s % numerator // r % denominator * s % denominator
  end function rational_multiply

  function rational_divide (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: res
    res = r % numerator * s % denominator // r % denominator * s % numerator
  end function rational_divide

  function rational_lt (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: r_simple
    type (rational) :: s_simple
    logical :: res
    r_simple = rational_simplify (r)
    s_simple = rational_simplify (s)
    res = r_simple % numerator * s_simple % denominator < &
        & s_simple % numerator * r_simple % denominator
  end function rational_lt

  function rational_le (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: r_simple
    type (rational) :: s_simple
    logical :: res
    r_simple = rational_simplify (r)
    s_simple = rational_simplify (s)
    res = r_simple % numerator * s_simple % denominator <= &
        & s_simple % numerator * r_simple % denominator
  end function rational_le

  function rational_gt (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: r_simple
    type (rational) :: s_simple
    logical :: res
    r_simple = rational_simplify (r)
    s_simple = rational_simplify (s)
    res = r_simple % numerator * s_simple % denominator > &
        & s_simple % numerator * r_simple % denominator
  end function rational_gt

  function rational_ge (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    type (rational) :: r_simple
    type (rational) :: s_simple
    logical :: res
    r_simple = rational_simplify (r)
    s_simple = rational_simplify (s)
    res = r_simple % numerator * s_simple % denominator >= &
        & s_simple % numerator * r_simple % denominator
  end function rational_ge

  function rational_eq (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    logical :: res
    res = r % numerator * s % denominator == s % numerator * r % denominator
  end function rational_eq

  function rational_ne (r, s) result (res)
    type (rational), intent (in) :: r
    type (rational), intent (in) :: s
    logical :: res
    res = r % numerator * s % denominator /= s % numerator * r % denominator
  end function rational_ne

  function rational_abs (r) result (res)
    type (rational), intent (in) :: r
    type (rational) :: res
    res = sign (r % numerator, r % denominator) // r % denominator
  end function rational_abs

  function rational_int (r) result (res)
    type (rational), intent (in) :: r
    integer :: res
    res = r % numerator / r % denominator
  end function rational_int

  function rational_modulo (r) result (res)
    type (rational), intent (in) :: r
    integer :: res
    res = modulo (r % numerator, r % denominator)
  end function rational_modulo

end module module_rational

Example:

program perfect_numbers

  use module_rational
  implicit none
  integer, parameter :: n_min = 2
  integer, parameter :: n_max = 2 ** 19 - 1
  integer :: n
  integer :: factor
  type (rational) :: sum

  do n = n_min, n_max
    sum = 1 // n
    factor = 2
    do
      if (factor * factor >= n) then
        exit
      end if
      if (modulo (n, factor) == 0) then
        sum = rational_simplify (sum + (1 // factor) + (factor // n))
      end if
      factor = factor + 1
    end do
    if (sum % numerator == 1 .and. sum % denominator == 1) then
      write (*, '(i0)') n
    end if
  end do

end program perfect_numbers
Output:
6
28
496
8128

Frink

Rational numbers are built into Frink and the numerator and denominator can be arbitrarily-sized. They are automatically simplified and collapsed into integers if necessary. All functions in the language can work with rational numbers. Rational numbers are treated as exact. Rational numbers can exist in complex numbers or intervals.

1/2 + 2/3
// 7/6 (approx. 1.1666666666666667)

1/2 + 1/2
// 1

5/sextillion + 3/quadrillion
// 600001/200000000000000000000 (exactly 3.000005e-15)

8^(1/3)
// 2    (note the exact integer result.)

GAP

Rational numbers are built-in.

2/3 in Rationals;
# true
2/3 + 3/4;
# 17/12

Go

Go does not have user defined operators. Go does however have a rational number type in the math/big package of the standard library. The big.Rat type supports the operations of the task, although typically with methods rather than operators:

  • Rat.Abs
  • Rat.Neg
  • Rat.Add
  • Rat.Sub
  • Rat.Mul
  • Rat.Quo
  • Rat.Cmp
  • Rat.SetInt

Code here implements the perfect number test described in the task using the standard library.

package main

import (
    "fmt"
    "math"
    "math/big"
)

func main() {
    var recip big.Rat
    max := int64(1 << 19)
    for candidate := int64(2); candidate < max; candidate++ {
        sum := big.NewRat(1, candidate)
        max2 := int64(math.Sqrt(float64(candidate)))
        for factor := int64(2); factor <= max2; factor++ {
            if candidate%factor == 0 {
                sum.Add(sum, recip.SetFrac64(1, factor))
                if f2 := candidate / factor; f2 != factor {
                    sum.Add(sum, recip.SetFrac64(1, f2))
                }
            }
        }
        if sum.Denom().Int64() == 1 {
            perfectstring := ""
            if sum.Num().Int64() == 1 {
                perfectstring = "perfect!"
            }
            fmt.Printf("Sum of recipr. factors of %d = %d exactly %s\n",
                candidate, sum.Num().Int64(), perfectstring)
        }
    }
}
Output:
Sum of recipr. factors of 6 = 1 exactly perfect!
Sum of recipr. factors of 28 = 1 exactly perfect!
Sum of recipr. factors of 120 = 2 exactly 
Sum of recipr. factors of 496 = 1 exactly perfect!
Sum of recipr. factors of 672 = 2 exactly 
Sum of recipr. factors of 8128 = 1 exactly perfect!
Sum of recipr. factors of 30240 = 3 exactly 
Sum of recipr. factors of 32760 = 3 exactly 
Sum of recipr. factors of 523776 = 2 exactly 

Groovy

Groovy does not provide any built-in facility for rational arithmetic. However, it does support arithmetic operator overloading. Thus it is not too hard to build a fairly robust, complete, and intuitive rational number class, such as the following:

class Rational extends Number implements Comparable {
    final BigInteger num, denom

    static final Rational ONE = new Rational(1)
    static final Rational ZERO = new Rational(0)

    Rational(BigDecimal decimal) {
        this(
        decimal.scale() < 0 ? decimal.unscaledValue() * 10 ** -decimal.scale() : decimal.unscaledValue(),
        decimal.scale() < 0 ? 1                                                : 10 ** decimal.scale()
        )
    }

    Rational(BigInteger n, BigInteger d = 1) {
        if (!d || n == null) { n/d }
        (num, denom) = reduce(n, d)
    }

    private List reduce(BigInteger n, BigInteger d) {
        BigInteger sign = ((n < 0) ^ (d < 0)) ? -1 : 1
        (n, d) = [n.abs(), d.abs()]
        BigInteger commonFactor = gcd(n, d)

        [n.intdiv(commonFactor) * sign, d.intdiv(commonFactor)]
    }

    Rational toLeastTerms() { reduce(num, denom) as Rational }

    private BigInteger gcd(BigInteger n, BigInteger m) {
        n == 0 ? m : { while(m%n != 0) { (n, m) = [m%n, n] }; n }()
    }

    Rational plus(Rational r) { [num*r.denom + r.num*denom, denom*r.denom] }
    Rational plus(BigInteger n) { [num + n*denom, denom] }
    Rational plus(Number n) { this + ([n] as Rational) }

    Rational next() { [num + denom, denom] }

    Rational minus(Rational r) { [num*r.denom - r.num*denom, denom*r.denom] }
    Rational minus(BigInteger n) { [num - n*denom, denom] }
    Rational minus(Number n) { this - ([n] as Rational) }

    Rational previous() { [num - denom, denom] }

    Rational multiply(Rational r) { [num*r.num, denom*r.denom] }
    Rational multiply(BigInteger n) { [num*n, denom] }
    Rational multiply(Number n) { this * ([n] as Rational) }


    Rational div(Rational r) { new Rational(num*r.denom, denom*r.num) }
    Rational div(BigInteger n) { new Rational(num, denom*n) }
    Rational div(Number n) { this / ([n] as Rational) }

    BigInteger intdiv(BigInteger n) { num.intdiv(denom*n) }

    Rational negative() { [-num, denom] }

    Rational abs() { [num.abs(), denom] }

    Rational reciprocal() { new Rational(denom, num) }

    Rational power(BigInteger n) {
        def (nu, de) = (n < 0 ? [denom, num] : [num, denom])*.power(n.abs())
        new Rational (nu, de)
    }

    boolean asBoolean() { num != 0 }

    BigDecimal toBigDecimal() { (num as BigDecimal)/(denom as BigDecimal) }

    BigInteger toBigInteger() { num.intdiv(denom) }

    Double toDouble() { toBigDecimal().toDouble() }
    double doubleValue() { toDouble() as double }

    Float toFloat() { toBigDecimal().toFloat() }
    float floatValue() { toFloat() as float }

    Integer toInteger() { toBigInteger().toInteger() }
    int intValue() { toInteger() as int }

    Long toLong() { toBigInteger().toLong() }
    long longValue() { toLong() as long }

    Object asType(Class type) {
        switch (type) {
            case this.class:              return this
            case [Boolean, Boolean.TYPE]: return asBoolean()
            case BigDecimal:              return toBigDecimal()
            case BigInteger:              return toBigInteger()
            case [Double, Double.TYPE]:   return toDouble()
            case [Float, Float.TYPE]:     return toFloat()
            case [Integer, Integer.TYPE]: return toInteger()
            case [Long, Long.TYPE]:       return toLong()
            case String:                  return toString()
            default: throw new ClassCastException("Cannot convert from type Rational to type " + type)
        }
    }

    boolean equals(o) { compareTo(o) == 0 }

    int compareTo(o) {
        o instanceof Rational
            ? compareTo(o as Rational)
            : o instanceof Number
                ? compareTo(o as Number)
                : (Double.NaN as int)
    }
    int compareTo(Rational r) { num*r.denom <=> denom*r.num }
    int compareTo(Number n) { num <=> denom*(n as BigInteger) }

    int hashCode() { [num, denom].hashCode() }

    String toString() {
        "${num}//${denom}"
    }
}

The following RationalCategory class allows for modification of regular Number behavior when interacting with Rational.

import org.codehaus.groovy.runtime.DefaultGroovyMethods

class RationalCategory {
    static Rational plus (Number a, Rational b) { ([a] as Rational) + b }
    static Rational minus (Number a, Rational b) { ([a] as Rational) - b }
    static Rational multiply (Number a, Rational b) { ([a] as Rational) * b }
    static Rational div (Number a, Rational b) { ([a] as Rational) / b  }

    static <T> T asType (Number a, Class<T> type) {
        type == Rational \
            ? [a] as Rational
                : DefaultGroovyMethods.asType(a, type)
    }
}

Test Program (mixes the RationalCategory methods into the Number class):

Number.metaClass.mixin RationalCategory

def x = [5, 20] as Rational
def y = [9, 12] as Rational
def z = [0, 10000] as Rational

println x
println y
println z
println (x <=> y)
println (x.compareTo(y))
assert x < y
assert x*3 == y
assert x*5.5 == 5.5*x
assert (z + 1) <= y*4
assert x + 1.3 == 1.3 + x
assert 24 - y == -(y - 24)
assert 3 / y == (y / 3).reciprocal()
assert x != y

println "x + y == ${x} + ${y} == ${x + y}"
println "x + z == ${x} + ${z} == ${x + z}"
println "x - y == ${x} - ${y} == ${x - y}"
println "x - z == ${x} - ${z} == ${x - z}"
println "x * y == ${x} * ${y} == ${x * y}"
println "y ** 3 == ${y} ** 3 == ${y ** 3}"
println "y ** -3 == ${y} ** -3 == ${y ** -3}"
println "x * z == ${x} * ${z} == ${x * z}"
println "x / y == ${x} / ${y} == ${x / y}"
try { print "x / z == ${x} / ${z} == "; println "${x / z}" }
catch (Throwable t) { println t.message }

println "-x == -${x} == ${-x}"
println "-y == -${y} == ${-y}"
println "-z == -${z} == ${-z}"

print "x as int == ${x} as int == "; println x.intValue()
print "x as double == ${x} as double == "; println x.doubleValue()
print "1 / x as int == 1 / ${x} as int == "; println x.reciprocal().intValue()
print "1.0 / x == 1.0 / ${x} == "; println x.reciprocal().doubleValue()
print "y as int == ${y} as int == "; println y.intValue()
print "y as double == ${y} as double == "; println y.doubleValue()
print "1 / y as int == 1 / ${y} as int == "; println y.reciprocal().intValue()
print "1.0 / y == 1.0 / ${y} == "; println y.reciprocal().doubleValue()
print "z as int == ${z} as int == "; println z.intValue()
print "z as double == ${z} as double == "; println z.doubleValue()
try { print "1 / z as int == 1 / ${z} as int == "; println z.reciprocal().intValue() }
catch (Throwable t) { println t.message }
try { print "1.0 / z == 1.0 / ${z} == "; println z.reciprocal().doubleValue() }
catch (Throwable t) { println t.message }

println "++x == ++ ${x} == ${++x}"
println "++y == ++ ${y} == ${++y}"
println "++z == ++ ${z} == ${++z}"
println "-- --x == -- -- ${x} == ${-- (--x)}"
println "-- --y == -- -- ${y} == ${-- (--y)}"
println "-- --z == -- -- ${z} == ${-- (--z)}"
println x
println y
println z

println (x <=> y)
assert x*3 == y
assert (z + 1) <= y*4
assert (x < y)

println 25 as Rational
println 25.0 as Rational
println 0.25 as Rational

def ε = 0.000000001  // tolerance (epsilon): acceptable "wrongness" to account for rounding error

def π = Math.PI
def α = π as Rational
assert (π - (α as BigDecimal)).abs() < ε
println π
println α
println (α.toBigDecimal())
println (α as BigDecimal)
println (α as Double)
println (α as double)
println (α as boolean)
println (z as boolean)
try { println (α as Date) }
catch (Throwable t) { println t.message }
try { println (α as char) }
catch (Throwable t) { println t.message }
Output:
1//4
3//4
0//1
-1
-1
x + y == 1//4 + 3//4 == 1//1
x + z == 1//4 + 0//1 == 1//4
x - y == 1//4 - 3//4 == -1//2
x - z == 1//4 - 0//1 == 1//4
x * y == 1//4 * 3//4 == 3//16
y ** 3 == 3//4 ** 3 == 27//64
y ** -3 == 3//4 ** -3 == 64//27
x * z == 1//4 * 0//1 == 0//1
x / y == 1//4 / 3//4 == 1//3
x / z == 1//4 / 0//1 == Division by zero
-x == -1//4 == -1//4
-y == -3//4 == -3//4
-z == -0//1 == 0//1
x as int == 1//4 as int == 0
x as double == 1//4 as double == 0.25
1 / x as int == 1 / 1//4 as int == 4
1.0 / x == 1.0 / 1//4 == 4.0
y as int == 3//4 as int == 0
y as double == 3//4 as double == 0.75
1 / y as int == 1 / 3//4 as int == 1
1.0 / y == 1.0 / 3//4 == 1.3333333333
z as int == 0//1 as int == 0
z as double == 0//1 as double == 0.0
1 / z as int == 1 / 0//1 as int == Division by zero
1.0 / z == 1.0 / 0//1 == Division by zero
++x == ++ 1//4 == 5//4
++y == ++ 3//4 == 7//4
++z == ++ 0//1 == 1//1
-- --x == -- -- 5//4 == -3//4
-- --y == -- -- 7//4 == -1//4
-- --z == -- -- 1//1 == -1//1
1//4
3//4
0//1
-1
25//1
25//1
1//4
3.141592653589793
884279719003555//281474976710656
3.141592653589793115997963468544185161590576171875
3.141592653589793115997963468544185161590576171875
3.141592653589793
3.141592653589793
true
false
Cannot convert from type Rational to type class java.util.Date
Cannot convert from type Rational to type char

The following uses the Rational class, with RationalCategory mixed into Number, to find all perfect numbers less than 219:

Number.metaClass.mixin RationalCategory

def factorize = { target ->
    assert target > 0
    if (target == 1L) { return [1L] }
    if ([2L, 3L].contains(target)) { return [1L, target] }
    def targetSqrt = Math.sqrt(target)
    def lowFactors = (2L..targetSqrt).findAll { (target % it) == 0 }

    if (!lowFactors) { return [1L, target] }
    def highFactors = lowFactors[-1..0].findResults { target.intdiv(it) } - lowFactors[-1]

    return [1L] + lowFactors + highFactors + [target]
}

def perfect = {
    def factors = factorize(it)
    2 as Rational == factors.sum{ factor -> new Rational(1, factor) } \
        ? [perfect: it, factors: factors]
        : null
}

def trackProgress = { if ((it % (100*1000)) == 0) { println it } else if ((it % 1000) == 0) { print "." } }

(1..(2**19)).findResults { trackProgress(it); perfect(it) }.each { println(); print it }
Output:
...................................................................................................100000
...................................................................................................200000
...................................................................................................300000
...................................................................................................400000
...................................................................................................500000
........................
[perfect:6, factors:[1, 2, 3, 6]]
[perfect:28, factors:[1, 2, 4, 7, 14, 28]]
[perfect:496, factors:[1, 2, 4, 8, 16, 31, 62, 124, 248, 496]]
[perfect:8128, factors:[1, 2, 4, 8, 16, 32, 64, 127, 254, 508, 1016, 2032, 4064, 8128]]

Haskell

Haskell provides a Rational type, which is really an alias for Ratio Integer (Ratio being a polymorphic type implementing rational numbers for any Integral type of numerators and denominators). The fraction is constructed using the % operator.

import Data.Ratio ((%))

-- Prints the first N perfect numbers.
main = do
  let n = 4
  mapM_ print $
    take
      n
      [ candidate
      | candidate <- [2 .. 2 ^ 19] 
      , getSum candidate == 1 ]
  where
    getSum candidate =
      1 % candidate +
      sum
        [ 1 % factor + 1 % (candidate `div` factor)
        | factor <- [2 .. floor (sqrt (fromIntegral candidate))] 
        , candidate `mod` factor == 0 ]

For a sample implementation of Ratio, see the Haskell 98 Report.

Icon and Unicon

The IPL provides support for rational arithmetic

  • The data type is called 'rational' not 'frac'.
  • Use the record constructor 'rational' to create a rational. Sign must be 1 or -1.
  • Neither Icon nor Unicon supports operator overloading. Augmented assignments make little sense w/o this.
  • Procedures include 'negrat' (unary -), 'addrat' (+), 'subrat' (-), 'mpyrat' (*), 'divrat' (modulo /).

Additional procedures are implemented here to complete the task:

  • 'makerat' (make), 'absrat' (abs), 'eqrat' (=), 'nerat' (~=), 'ltrat' (<), 'lerat' (<=), 'gerat' (>=), 'gtrat' (>)
procedure main()
   limit := 2^19

   write("Perfect numbers up to ",limit," (using rational arithmetic):")
   every write(is_perfect(c := 2 to limit))
   write("End of perfect numbers")

   # verify the rest of the implementation

   zero := makerat(0)          # from integer
   half := makerat(0.5)        # from real
   qtr  := makerat("1/4")      # from strings ...
   one  := makerat("1")
   mone := makerat("-1")

   verifyrat("eqrat",zero,zero)
   verifyrat("ltrat",zero,half)
   verifyrat("ltrat",half,zero)
   verifyrat("gtrat",zero,half)
   verifyrat("gtrat",half,zero)
   verifyrat("nerat",zero,half)
   verifyrat("nerat",zero,zero)
   verifyrat("absrat",mone,)

end

procedure is_perfect(c)       #: test for perfect numbers using rational arithmetic
   rsum := rational(1, c, 1)
   every f := 2 to sqrt(c) do 
      if 0 = c % f then 
         rsum := addrat(rsum,addrat(rational(1,f,1),rational(1,integer(c/f),1)))   
   if rsum.numer = rsum.denom = 1 then 
      return c
end
Output:
Perfect numbers up to 524288 (using rational arithmetic):
6
28
496
8128
End of perfect numbers
Testing eqrat( (0/1), (0/1) ) ==> returned (0/1)
Testing ltrat( (0/1), (1/2) ) ==> returned (1/2)
Testing ltrat( (1/2), (0/1) ) ==> failed
Testing gtrat( (0/1), (1/2) ) ==> failed
Testing gtrat( (1/2), (0/1) ) ==> returned (0/1)
Testing nerat( (0/1), (1/2) ) ==> returned (1/2)
Testing nerat( (0/1), (0/1) ) ==> failed
Testing absrat( (-1/1),  ) ==> returned (1/1)

The following task functions are missing from the IPL:

procedure verifyrat(p,r1,r2)  #: verification tests for rational procedures
return write("Testing ",p,"( ",rat2str(r1),", ",rat2str(\r2) | &null," ) ==> ","returned " || rat2str(p(r1,r2)) | "failed")  
end

procedure makerat(x)          #: make rational (from integer, real, or strings)
local n,d
static c
initial c := &digits++'+-'

   return case type(x) of {
             "real"    : real2rat(x)
             "integer" : ratred(rational(x,1,1))
             "string"  : if x ? ( n := integer(tab(many(c))), ="/", d := integer(tab(many(c))), pos(0)) then  
                            ratred(rational(n,d,1)) 
                         else 
                            makerat(numeric(x))  
          }
end

procedure absrat(r1)          #: abs(rational)
   r1 := ratred(r1)
   r1.sign := 1
   return r1
end

invocable all                 #  for string invocation

procedure xoprat(op,r1,r2)    #: support procedure for binary operations that cross denominators
   local numer, denom, div

   r1 := ratred(r1)
   r2 := ratred(r2)

   return if op(r1.numer * r2.denom,r2.numer * r1.denom) then r2   # return right argument on success
end

procedure eqrat(r1,r2)        #: rational r1 = r2
return xoprat("=",r1,r2)
end

procedure nerat(r1,r2)        #: rational r1 ~= r2
return xoprat("~=",r1,r2)
end

procedure ltrat(r1,r2)        #: rational r1 < r2
return xoprat("<",r1,r2)
end

procedure lerat(r1,r2)        #: rational r1 <= r2
return xoprat("<=",r1,r2)
end

procedure gerat(r1,r2)        #: rational r1 >= r2
return xoprat(">=",r1,r2)
end

procedure gtrat(r1,r2)        #: rational r1 > r2
return xoprat(">",r1,r2)
end

link rational
The provides rational and gcd in numbers. Record definition and usage is shown below:
   record rational(numer, denom, sign)        # rational type

   addrat(r1,r2) # Add rational numbers r1 and r2.
   divrat(r1,r2) # Divide rational numbers r1 and r2.
   medrat(r1,r2) # Form mediant of r1 and r2.
   mpyrat(r1,r2) # Multiply rational numbers r1 and r2.
   negrat(r)     # Produce negative of rational number r.
   rat2real(r)   # Produce floating-point approximation of r
   rat2str(r)    # Convert the rational number r to its string representation.
   real2rat(v,p) # Convert real to rational with precision p (default 1e-10). Warning: excessive p gives ugly fractions
   reciprat(r)   # Produce the reciprocal of rational number r.
   str2rat(s)    # Convert the string representation (such as "3/2") to a rational number
   subrat(r1,r2) # Subtract rational numbers r1 and r2.

   gcd(i, j)     # returns greatest common divisor of i and j

J

Rational numbers in J may be formed from fixed precision integers by first upgrading them to arbitrary precision integers and then dividing them:

  (x: 3) % (x: -4)
_3r4
   3 %&x: -4
_3r4

Note that the syntax is analogous to the syntax for floating point numbers, but uses r to separate the numerator and denominator instead of e to separate the mantissa and exponent. Thus:

   | _3r4             NB. absolute value
3r4
   -2r5               NB. negation
_2r5
   3r4+2r5            NB. addition
23r20
   3r4-2r5            NB. subtraction
7r20
   3r4*2r5            NB. multiplication
3r10
   3r4%2r5            NB. division
15r8
   3r4 <.@% 2r5       NB. integer division
1
   3r4 (-~ <.)@% 2r5  NB. remainder
_7r8
   3r4 < 2r5          NB. less than
0
   3r4 <: 2r5         NB. less than or equal
0
   3r4 > 2r5          NB. greater than
1
   3r4 >: 2r5         NB. greater than or equal
1
   3r4 = 2r5          NB. equal
0
   3r4 ~: 2r5         NB. not equal
1

You can also coerce numbers directly to rational using x: (or to integer or floating point as appropriate using its inverse)

   x: 3%4
3r4
   x:inv 3%4
0.75

Increment and decrement are also included in the language, but you could just as easily add or subtract 1:

   >: 3r4
7r4
   <: 3r4
_1r4

J does not encourage the use of specialized mutators, but those could also be defined:

mutadd=:adverb define
   (m)=: (".m)+y
)

mutsub=:adverb define
   (m)=: (".m)-y
)

Note that the name whose association is being modified in this fashion needs to be quoted (or you can use an expression to provide the name):

   n=: 3r4
   'n' mutadd 1
7r4
   'n' mutsub 1
3r4
   'n' mutsub 1
_1r4

(Bare words to the immediate left of the assignment operator are implicitly quoted - but this is just syntactic sugar because that is such an overwhelmingly common case.)

That said, note that J's floating point numbers work just fine for the stated problem:

   is_perfect_rational=: 2 = (1 + i.) +/@:%@([ #~ 0 = |) ]

Faster version (but the problem, as stated, is still tremendously inefficient):

factors=: */&>@{@((^ i.@>:)&.>/)@q:~&__
is_perfect_rational=: 2= +/@:%@,@factors

Exhaustive testing would take forever:

   I.is_perfect_rational@"0 i.2^19
6 28 496 8128
   I.is_perfect_rational@x:@"0 i.2^19x
6 28 496 8128

More limited testing takes reasonable amounts of time:

   (#~ is_perfect_rational"0) (* <:@+:) 2^i.10x
6 28 496 8128

Java

Uses BigRational class: Arithmetic/Rational/Java

public class BigRationalFindPerfectNumbers {
    public static void main(String[] args) {
        int MAX_NUM = 1 << 19;
        System.out.println("Searching for perfect numbers in the range [1, " + (MAX_NUM - 1) + "]");

        BigRational TWO = BigRational.valueOf(2);
        for (int i = 1; i < MAX_NUM; i++) {
            BigRational reciprocalSum = BigRational.ONE;
            if (i > 1)
                reciprocalSum = reciprocalSum.add(BigRational.valueOf(i).reciprocal());
            int maxDivisor = (int) Math.sqrt(i);
            if (maxDivisor >= i)
                maxDivisor--;

            for (int divisor = 2; divisor <= maxDivisor; divisor++) {
                if (i % divisor == 0) {
                    reciprocalSum = reciprocalSum.add(BigRational.valueOf(divisor).reciprocal());
                    int dividend = i / divisor;
                    if (divisor != dividend)
                        reciprocalSum = reciprocalSum.add(BigRational.valueOf(dividend).reciprocal());
                }
            }
            if (reciprocalSum.equals(TWO))
                System.out.println(String.valueOf(i) + " is a perfect number");
        }
    }
}
Output:
Searching for perfect numbers in the range [1, 524287]
6 is a perfect number
28 is a perfect number
496 is a perfect number
8128 is a perfect number

JavaScript

[This section is included from a subpage and should be edited there, not here.]
The core of the Rational class

<lang javascript>// the constructor function Rational(numerator, denominator) {

   if (denominator === undefined)
       denominator = 1;
   else if (denominator == 0)
       throw "divide by zero";
   this.numer = numerator;
   if (this.numer == 0)
       this.denom = 1;
   else
       this.denom = denominator;
   this.normalize();

}

// getter methods Rational.prototype.numerator = function() {return this.numer}; Rational.prototype.denominator = function() {return this.denom};

// clone a rational Rational.prototype.dup = function() {

   return new Rational(this.numerator(), this.denominator()); 

};

// conversion methods Rational.prototype.toString = function() {

   if (this.denominator() == 1) {
       return this.numerator().toString();
   } else {
       // implicit conversion of numbers to strings
       return this.numerator() + '/' + this.denominator()
   }

}; Rational.prototype.toFloat = function() {return eval(this.toString())} Rational.prototype.toInt = function() {return Math.floor(this.toFloat())};

// reduce Rational.prototype.normalize = function() {

   // greatest common divisor
   var a=Math.abs(this.numerator()), b=Math.abs(this.denominator())
   while (b != 0) {
       var tmp = a;
       a = b;
       b = tmp % b;
   }
   // a is the gcd
   this.numer /= a;
   this.denom /= a;
   if (this.denom < 0) {
       this.numer *= -1;
       this.denom *= -1;
   }
   return this;

}

// absolute value // returns a new rational Rational.prototype.abs = function() {

   return new Rational(Math.abs(this.numerator()), this.denominator());

};

// inverse // returns a new rational Rational.prototype.inv = function() {

   return new Rational(this.denominator(), this.numerator());

};

// // arithmetic methods

// variadic, modifies receiver Rational.prototype.add = function() {

   for (var i = 0; i < arguments.length; i++) {
       this.numer = this.numer * arguments[i].denominator() + this.denom * arguments[i].numerator();
       this.denom = this.denom * arguments[i].denominator();
   }
   return this.normalize();

};

// variadic, modifies receiver Rational.prototype.subtract = function() {

   for (var i = 0; i < arguments.length; i++) {
       this.numer = this.numer * arguments[i].denominator() - this.denom * arguments[i].numerator();
       this.denom = this.denom * arguments[i].denominator();
   }
   return this.normalize();

};

// unary "-" operator // returns a new rational Rational.prototype.neg = function() {

   return (new Rational(0)).subtract(this);

};

// variadic, modifies receiver Rational.prototype.multiply = function() {

   for (var i = 0; i < arguments.length; i++) {
       this.numer *= arguments[i].numerator();
       this.denom *= arguments[i].denominator();
   }
   return this.normalize();

};

// modifies receiver Rational.prototype.divide = function(rat) {

   return this.multiply(rat.inv());

}


// increment // modifies receiver Rational.prototype.inc = function() {

   this.numer += this.denominator();
   return this.normalize();

}

// decrement // modifies receiver Rational.prototype.dec = function() {

   this.numer -= this.denominator();
   return this.normalize();

}

// // comparison methods

Rational.prototype.isZero = function() {

   return (this.numerator() == 0);

} Rational.prototype.isPositive = function() {

   return (this.numerator() > 0);

} Rational.prototype.isNegative = function() {

   return (this.numerator() < 0);

}

Rational.prototype.eq = function(rat) {

   return this.dup().subtract(rat).isZero();

} Rational.prototype.ne = function(rat) {

   return !(this.eq(rat));

} Rational.prototype.lt = function(rat) {

   return this.dup().subtract(rat).isNegative();

} Rational.prototype.gt = function(rat) {

   return this.dup().subtract(rat).isPositive();

} Rational.prototype.le = function(rat) {

   return !(this.gt(rat));

} Rational.prototype.ge = function(rat) {

   return !(this.lt(rat));

}</lang>

Testing

<lang javascript>function assert(cond, msg) { if (!cond) throw msg; }

print('testing') var a, b, c, d, e, f;

//test creation a = new Rational(0); assert(a.toString() == "0", "Rational(0).toString() == '0'") a = new Rational(2); assert(a.toString() == "2", "Rational(2).toString() == '2'") a = new Rational(1,2); assert(a.toString() == "1/2", "Rational(1,2).toString() == '1/2'") b = new Rational(2,-12); assert(b.toString() == "-1/6", "Rational(1,6).toString() == '1/6'") f = new Rational(0,9)

a = new Rational(1,3) b = new Rational(1,2) c = new Rational(1,3)

assert(!(a.eq(b)), "1/3 == 1/2") assert(a.eq(c), "1/3 == 1/3") assert(a.ne(b), "1/3 != 1/2") assert(!(a.ne(c)), "1/3 != 1/3") assert(a.lt(b), "1/3 < 1/2") assert(!(b.lt(a)), "1/2 < 1/3") assert(!(a.lt(c)), "1/3 < 1/3") assert(!(a.gt(b)), "1/3 > 1/2") assert(b.gt(a), "1/2 > 1/3") assert(!(a.gt(c)), "1/3 > 1/3")

assert(a.le(b), "1/3 <= 1/2") assert(!(b.le(a)), "1/2 <= 1/3") assert(a.le(c), "1/3 <= 1/3") assert(!(a.ge(b)), "1/3 >= 1/2") assert(b.ge(a), "1/2 >= 1/3") assert(a.ge(c), "1/3 >= 1/3")

a = new Rational(1,2) b = new Rational(1,6) a.add(b); assert(a.eq(new Rational(2,3)), "1/2 + 1/6 == 2/3") c = a.neg(); assert(a.eq(new Rational(2,3)), "neg(1/2) == -1/2")

            assert(c.eq(new Rational(2,-3)), "neg(1/2) == -1/2")

d = c.abs(); assert(c.eq(new Rational(-2,3)), "abs(neg(1/2)) == 1/2")

            assert(d.eq(new Rational(2,3)), "abs(neg(1/2)) == 1/2")

b.subtract(a); assert(b.eq(new Rational(-1,2)), "1/6 - 1/2 == -1/3")

c = a.neg().abs(); assert(c.eq(a), "abs(neg(1/2)) == 1/2") c = (new Rational(-1,3)).inv(); assert(c.toString() == '-3', "inv(1/6 - 1/2) == -3") try {

   e = f.inv();
   throw "should have been an error: " +f + '.inv() = ' + e

} catch (e) {

   assert(e == "divide by zero", "0.inv() === error")

}

b = new Rational(1,6) b.add(new Rational(2,3), new Rational(4,2)); assert(b.toString() == "17/6", "1/6+2/3+4/2 == 17/6");

a = new Rational(1,3); b = new Rational(1,6) c = new Rational(5,6); d = new Rational(1/5); e = new Rational(2); f = new Rational(0,9);


assert(c.dup().multiply(d).eq(b), "5/6 * 1/5 = 1/6") assert(c.dup().multiply(d,e).eq(a), "5/6 * 1/5 *2 = 1/3") assert(c.dup().multiply(d,e,f).eq(f), "5/6 * 1/5 *2*0 = 0")

c.divide(new Rational(5)); assert(c.eq(b), "5/6 / 5 = 1/6b")

try {

   e = c.divide(f)
   throw "should have been an error: " + c + "/" + f + '= ' + e

} catch (e) {

   assert(e == "divide by zero", "0.inv() === error")

}


print('all tests passed');</lang>

Finding perfect numbers

<lang javascript>function factors(num) {

   var factors = new Array();
   var sqrt = Math.floor(Math.sqrt(num)); 
   for (var i = 1; i <= sqrt; i++) {
       if (num % i == 0) {
           factors.push(i);
           if (num / i != i) 
               factors.push(num / i);
       }
   }
   factors.sort(function(a,b){return a-b});  // numeric sort
   return factors;

}

function isPerfect(n) {

   var sum = new Rational(0);
   var fctrs = factors(n);
   for (var i = 0; i < fctrs.length; i++) 
       sum.add(new Rational(1, fctrs[i]));
   // note, fctrs includes 1, so sum should be 2
   return sum.toFloat() == 2.0;

}

// find perfect numbers less than 2^19 for (var n = 2; n < Math.pow(2,19); n++)

   if (isPerfect(n))
       print("perfect: " + n);

// test 5th perfect number var n = Math.pow(2,12) * (Math.pow(2,13) - 1); if (isPerfect(n))

   print("perfect: " + n);</lang>
Output:
perfect: 6
perfect: 28
perfect: 496
perfect: 8128
perfect: 33550336

jq

Works with: jq

Works with gojq, the Go implementation of jq

In this entry, a jq module for rational arithmetic is first presented. It can be included or imported using jq's "include" or "import" directives. The module includes functions for taking square roots and for converting a rational to a decimal string or decimal string approximation, and is sufficient for the jq solution at Faulhaber's_triangle#jq.

The constructor is named "r" rather than "frac", mainly because "r" is short and handy for what is here more than a simple constructor (it can also be used for normalization and to divide one rational by another), and because name conflicts can easily be resolved using jq's module system.

The other operators for working with rationals also begin with the letter "r":

Comparisons

  • `requal`, `rgreaterthan`, `rgreaterthanOrEqual`, `rlessthan`, `rlessthanOrEqual`

Printing

  • `rpp` for pretty-printing
  • `r_to_decimal` for a decimal string representation

Unary

  • `rabs` for unary `abs`
  • `rfloor` like `floor`
  • `rinv` for unary inverse
  • `rminus` for unary minus
  • `rround` for rounding

Arithmetic

  • `radd` for addition
  • `rminus` for subtraction
  • `rmult` for multiplication
  • `rdiv` for division
  • `rsqrt` for square roots


In the following notes, "Rational" refers to a reduced-form rational represented by a JSON object of the form {n:$n, d:$d} where n signifies the numerator and d the denominator, and $d > 0.

The notation `$p // $q` is also used, and this is the form used for pretty-printing by the filter rpp/0.

All the "r"-prefixed functions defined here are polymorphic in the sense that an integer or rational can be used where a Rational would normally be expected. This may be especially useful in the case of requal/2.

module {"name": "Rational"};

# a and b are assumed to be non-zero integers 
def gcd(a; b):
  # subfunction expects [a,b] as input
  # i.e. a ~ .[0] and b ~ .[1]
  def rgcd: if .[1] == 0 then .[0]
         else [.[1], .[0] % .[1]] | rgcd
         end;
  [a,b] | rgcd;

# To take advantage of gojq's support for accurate integer division:
def idivide($j):
  . as $i
  | ($i % $j) as $mod
  | ($i - $mod) / $j ;

# To take advantage of gojq's arbitrary-precision integer arithmetic:
def power($b): . as $in | reduce range(0;$b) as $i (1; . * $in);

# $p should be an integer or a rational
# $q should be a non-zero integer or a rational
# Output:  a Rational: $p // $q
def r($p;$q):
  def r: if type == "number" then {n: ., d: 1} else . end;
  # The remaining subfunctions assume all args are Rational
  def n: if .d < 0 then {n: -.n, d: -.d} else . end;
  def rdiv($a;$b):
    ($a.d * $b.n) as $denom
    | if $denom==0 then "r: division by 0" | error
    else r($a.n * $b.d; $denom)
    end;
  if $q == 1 and ($p|type) == "number" then {n: $p, d: 1}
  elif $q == 0 then "r: denominator cannot be 0" | error
  else if ($p|type == "number") and ($q|type == "number")
       then gcd($p;$q) as $g
       | {n: ($p/$g), d: ($q/$g)} | n
       else rdiv($p|r; $q|r)
       end
  end;

# Polymorphic (integers and rationals in general)
def requal($a; $b):
  if $a | type == "number" and $b | type == "number" then $a == $b
  else r($a;1) == r($b;1)
  end;

# Input: a Rational
# Output: a Rational with a denominator that has no more than $digits digits
# and such that |rBefore - rAfter| < 1/(10|power($digits)
# where $digits should be a positive integer.
def rround($digits):
  if .d | length > $digits 
  then (10|power($digits)) as $p
  | .d as $d
  | r($p * .n | idivide($d); $p)
  else . end;

# Polymorphic; see also radd/0
def radd($a; $b):
  def r: if type == "number" then {n: ., d: 1} else . end;
    ($a|r) as {n: $na, d: $da}
  | ($b|r) as {n: $nb, d: $db}
  | r( ($na * $db) + ($nb * $da); $da * $db );

# Polymorphic; see also rmult/0
def rmult($a; $b):
  def r: if type == "number" then {n: ., d: 1} else . end;
    ($a|r) as {n: $na, d: $da}
  | ($b|r) as {n: $nb, d: $db}
  | r( $na * $nb; $da * $db ) ;

# Input: an array of rationals (integers and/or Rationals)
# Output: a Rational computed using left-associativity
def rmult:
  if length == 0 then r(1;1)
  elif length == 1 then r(.[0]; 1)  # ensure the result is Rational
  else .[0] as $first
  | reduce .[1:][] as $x ($first; rmult(.; $x))
  end;

# Input: an array of rationals (integers and/or Rationals)
# Output: a Rational computed using left-associativity
def radd:
  if length == 0 then r(0;1)
  elif length == 1 then r(.[0]; 1) # ensure the result is Rational
  else .[0] as $first
  | reduce .[1:][] as $x ($first; radd(. ; $x))
  end;

def rabs: r(.;1) | r(.n|length; .d|length);

def rminus: r(-1 * .n; .d);

def rminus($a; $b): radd($a; rmult(-1; $b));

# Note that rinv does not check for division by 0
def rinv: r(1; .);

def rdiv($a; $b): r($a; $b);

# Input: an integer or a Rational, $p
# Output: $p < $q
def rlessthan($q):
  # lt($b) assumes . and $b have the same sign 
  def lt($b):
    . as $a
    | ($a.n * $b.d) < ($b.n * $a.d);

  if $q|type == "number" then rlessthan(r($q;1))
  else if type == "number" then r(.;1) else . end
  | if .n < 0
    then if ($q.n >= 0) then true
         else . as $p | ($q|rminus | rlessthan($p|rminus))
         end
    else lt($q)
    end
  end;

def rgreaterthan($q):
  . as $p | $q | rlessthan($p);

def rlessthanOrEqual($q): requal(.;$q) or rlessthan($q);
def rgreaterthanOrEqual($q): requal(.;$q) or rgreaterthan($q);

# Input: non-negative integer or Rational
def rsqrt(precision):
  r(.;1) as $n
  | (precision + 1) as $digits
  | def update: rmult( r(1;2); radd(.x; rdiv($n; .x))) | rround($digits);

  | def update: rmult( r(1;2); radd(.x; rdiv($n; .x)));

  r(1; 10|power(precision)) as $p
  | { x: .}
  | .root = update
  | until( rminus(.root; .x) | rabs | rlessthan($p);
      .x = .root
      | .root = update )
  | .root ;

# Use native floats
# q.v. r_to_decimal(precision)
def r_to_decimal: .n / .d;

# Input: a Rational, or {n, d} in general, or an integer.
# Output: a string representation of the input as a decimal number.
# If the input is a number, it is simply converted to a string.
# Otherwise, $precision determines the number of digits after the decimal point,
# obtained by truncating, but trailing 0s are omitted.
# Examples assuming $digits is 5:
#  -0//1 => "0"
#   2//1 => "2"
#   1//2 => "0.5"
#   1//3 => "0.33333"
#   7//9 => "0.77777"
#   1//100 => "0.01"
#  -1//10 => "-0.1"
#   1//1000000 => "0."
def r_to_decimal($digits):
  if .n == 0 # captures the annoying case of -0
  then "0"
  elif type == "number" then tostring
  elif .d < 0 then {n: -.n, d: -.d}|r_to_decimal($digits)
  elif .n < 0
  then "-" + ((.n = -.n) | r_to_decimal($digits))
  else (10|power($digits)) as $p
  | .d as $d
  | if $d == 1 then .n|tostring
    else ($p * .n | idivide($d) | tostring) as $n
    | ($n|length) as $nlength
    | (if $nlength > $digits then $n[0:$nlength-$digits] + "." + $n[$nlength-$digits:]
       else "0." + ("0"*($digits - $nlength) + $n)
       end) | sub("0+$";"")
    end
  end;

# Assume . is an integer or in canonical form
def rfloor:
  if type == "number" then r(.;1)
  elif 0 == .n or (0 < .n and .n < .d) then r(0;1)
  elif 0 < .n or (.n % .d == 0) then .d as $d | r(.n | idivide($d); 1)
  else rminus( r( - .n; .d) | rfloor | rminus; 1)
  end;

# pretty print ala Julia
def rpp: "\(.n) // \(.d)";

Perfect Numbers

# divisors as an unsorted stream
def divisors:
  if . == 1 then 1
  else . as $n
  | label $out
  | range(1; $n) as $i
  | ($i * $i) as $i2
  | if $i2 > $n then break $out
    else if $i2 == $n
         then $i
         elif ($n % $i) == 0
         then $i, ($n/$i)
         else empty
	 end
    end
  end;

def is_perfect:
  requal(2; [divisors | r(1;. )] | radd);
 
# Example:
range(1;pow(2;19)) | select( is_perfect )
Output:
6
28
496
8128

Julia

Julia has native support for rational numbers. Rationals are expressed as m//n, where m and n are integers. In addition to supporting most of the usual mathematical functions in a natural way on rationals, the methods num and den provide the fully reduced numerator and denominator of a rational value.

Works with: Julia version 1.2
using Primes
divisors(n) = foldl((a, (p, e)) -> vcat((a * [p^i for i in 0:e]')...), factor(n), init=[1])

isperfect(n) = sum(1 // d for d in divisors(n)) == 2

lo, hi = 2, 2^19
println("Perfect numbers between ", lo, " and ", hi, ": ", collect(filter(isperfect, lo:hi)))
Output:
Perfect numbers between 2 and 524288: [6, 28, 496, 8128]

Kotlin

As it's not possible to define arbitrary symbols such as // to be operators in Kotlin, we instead use infix functions idiv (for Ints) and ldiv (for Longs) as a shortcut to generate Frac instances.

// version 1.1.2

fun gcd(a: Long, b: Long): Long = if (b == 0L) a else gcd(b, a % b)

infix fun Long.ldiv(denom: Long) = Frac(this, denom)

infix fun Int.idiv(denom: Int) = Frac(this.toLong(), denom.toLong())

fun Long.toFrac() = Frac(this, 1)

fun Int.toFrac() = Frac(this.toLong(), 1)

class Frac : Comparable<Frac> {
    val num: Long
    val denom: Long

    companion object {
        val ZERO = Frac(0, 1)
        val ONE  = Frac(1, 1)
    }
 
    constructor(n: Long, d: Long) {
        require(d != 0L)
        var nn = n
        var dd = d
        if (nn == 0L) {
            dd = 1
        }
        else if (dd < 0) {
            nn = -nn
            dd = -dd
        } 
        val g = Math.abs(gcd(nn, dd))
        if (g > 1) {
            nn /= g
            dd /= g
        }
        num = nn
        denom = dd
    }

    constructor(n: Int, d: Int) : this(n.toLong(), d.toLong())
 
    operator fun plus(other: Frac) = 
        Frac(num * other.denom + denom * other.num, other.denom * denom)

    operator fun unaryPlus() = this

    operator fun unaryMinus() = Frac(-num, denom)

    operator fun minus(other: Frac) = this + (-other)

    operator fun times(other: Frac) = Frac(this.num * other.num, this.denom * other.denom)

    operator fun rem(other: Frac) = this - Frac((this / other).toLong(), 1) * other

    operator fun inc() = this + ONE
    operator fun dec() = this - ONE

    fun inverse(): Frac {
        require(num != 0L)
        return Frac(denom, num)
    }

    operator fun div(other: Frac) = this * other.inverse()
    
    fun abs() = if (num >= 0) this else -this

    override fun compareTo(other: Frac): Int {
        val diff = this.toDouble() - other.toDouble()
        return when {
            diff < 0.0  -> -1
            diff > 0.0  -> +1
            else        ->  0
        } 
    }

    override fun equals(other: Any?): Boolean {
       if (other == null || other !is Frac) return false 
       return this.compareTo(other) == 0
    }

    override fun hashCode() = num.hashCode() xor denom.hashCode()                       

    override fun toString() = if (denom == 1L) "$num" else "$num/$denom"
 
    fun toDouble() = num.toDouble() / denom

    fun toLong() = num / denom
}

fun isPerfect(n: Long): Boolean {
    var sum = Frac(1, n)
    val limit = Math.sqrt(n.toDouble()).toLong()
    for (i in 2L..limit) {
        if (n % i == 0L) sum += Frac(1, i) + Frac(1, n / i) 
    }
    return sum == Frac.ONE
} 

fun main(args: Array<String>) {
    var frac1 = Frac(12, 3)
    println ("frac1 = $frac1")
    var frac2 = 15 idiv 2 
    println("frac2 = $frac2")
    println("frac1 <= frac2 is ${frac1 <= frac2}")
    println("frac1 >= frac2 is ${frac1 >= frac2}")
    println("frac1 == frac2 is ${frac1 == frac2}")
    println("frac1 != frac2 is ${frac1 != frac2}")
    println("frac1 + frac2 = ${frac1 + frac2}")
    println("frac1 - frac2 = ${frac1 - frac2}")
    println("frac1 * frac2 = ${frac1 * frac2}")
    println("frac1 / frac2 = ${frac1 / frac2}")
    println("frac1 % frac2 = ${frac1 % frac2}")
    println("inv(frac1)    = ${frac1.inverse()}")
    println("abs(-frac1)   = ${-frac1.abs()}")
    println("inc(frac2)    = ${++frac2}")
    println("dec(frac2)    = ${--frac2}")
    println("dbl(frac2)    = ${frac2.toDouble()}")
    println("lng(frac2)    = ${frac2.toLong()}")
    println("\nThe Perfect numbers less than 2^19 are:")
    // We can skip odd numbers as no known perfect numbers are odd 
    for (i in 2 until (1 shl 19) step 2) { 
        if (isPerfect(i.toLong())) print("  $i")
    } 
    println() 
}
Output:
frac1 = 4
frac2 = 15/2
frac1 <= frac2 is true
frac1 >= frac2 is false
frac1 == frac2 is false
frac1 != frac2 is true
frac1 + frac2 = 23/2
frac1 - frac2 = -7/2
frac1 * frac2 = 30
frac1 / frac2 = 8/15
frac1 % frac2 = 4
inv(frac1)    = 1/4
abs(-frac1)   = -4
inc(frac2)    = 17/2
dec(frac2)    = 15/2
dbl(frac2)    = 7.5
lng(frac2)    = 7

The Perfect numbers less than 2^19 are:
  6  28  496  8128

Liberty BASIC

Testing all numbers up to 2 ^ 19 takes an excessively long time.

n=2^19
for testNumber=1 to n
    sum$=castToFraction$(0)
    for factorTest=1 to sqr(testNumber)
        if GCD(factorTest,testNumber)=factorTest then sum$=add$(sum$,add$(reciprocal$(castToFraction$(factorTest)),reciprocal$(castToFraction$(testNumber/factorTest))))
    next factorTest
    if equal(sum$,castToFraction$(2))=1 then print testNumber
next testNumber
end

function abs$(a$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    bNumerator=abs(aNumerator)
    bDenominator=abs(aDenominator)
    b$=str$(bNumerator)+"/"+str$(bDenominator)
    abs$=simplify$(b$)
end function

function negate$(a$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    bNumerator=-1*aNumerator
    bDenominator=aDenominator
    b$=str$(bNumerator)+"/"+str$(bDenominator)
    negate$=simplify$(b$)
end function

function add$(a$,b$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    bNumerator=val(word$(b$,1,"/"))
    bDenominator=val(word$(b$,2,"/"))
    cNumerator=(aNumerator*bDenominator+bNumerator*aDenominator)
    cDenominator=aDenominator*bDenominator
    c$=str$(cNumerator)+"/"+str$(cDenominator)
    add$=simplify$(c$)
end function

function subtract$(a$,b$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    bNumerator=val(word$(b$,1,"/"))
    bDenominator=val(word$(b$,2,"/"))
    cNumerator=(aNumerator*bDenominator-bNumerator*aDenominator)
    cDenominator=aDenominator*bDenominator
    c$=str$(cNumerator)+"/"+str$(cDenominator)
    subtract$=simplify$(c$)
end function

function multiply$(a$,b$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    bNumerator=val(word$(b$,1,"/"))
    bDenominator=val(word$(b$,2,"/"))
    cNumerator=aNumerator*bNumerator
    cDenominator=aDenominator*bDenominator
    c$=str$(cNumerator)+"/"+str$(cDenominator)
    multiply$=simplify$(c$)
end function

function divide$(a$,b$)
    divide$=multiply$(a$,reciprocal$(b$))
end function

function simplify$(a$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    gcd=GCD(aNumerator,aDenominator)
    if aNumerator<0 and aDenominator<0 then gcd=-1*gcd
    bNumerator=aNumerator/gcd
    bDenominator=aDenominator/gcd
    b$=str$(bNumerator)+"/"+str$(bDenominator)
    simplify$=b$
end function

function reciprocal$(a$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    reciprocal$=str$(aDenominator)+"/"+str$(aNumerator)
end function

function equal(a$,b$)
    if simplify$(a$)=simplify$(b$) then equal=1:else equal=0
end function

function castToFraction$(a)
    do
        exp=exp+1
        a=a*10
    loop until a=int(a)
    castToFraction$=simplify$(str$(a)+"/"+str$(10^exp))
end function

function castToReal(a$)
    aNumerator=val(word$(a$,1,"/"))
    aDenominator=val(word$(a$,2,"/"))
    castToReal=aNumerator/aDenominator
end function

function castToInt(a$)
    castToInt=int(castToReal(a$))
end function

function GCD(a,b)
    if a=0 then
        GCD=1
    else
        if a>=b then
            while b
                c = a
                a = b
                b = c mod b
                GCD = abs(a)
            wend
        else
            GCD=GCD(b,a)
        end if
    end if
end function

Lingo

A new 'frac' data type can be implemented like this:

-- parent script "Frac"
property num
property denom

----------------------------------------
-- @constructor
-- @param {integer} numerator
-- @param {integer} [denominator=1]
----------------------------------------
on new (me, numerator, denominator)
  if voidP(denominator) then denominator = 1
  if denominator=0 then return VOID -- rule out division by zero
  g = me._gcd(numerator, denominator)
  if g<>0 then
    numerator = numerator/g
    denominator = denominator/g
  else
    numerator = 0
    denominator = 1
  end if
  if denominator<0 then
    numerator = -numerator
    denominator = -denominator
  end if
  me.num = numerator
  me.denom = denominator
  return me
end

----------------------------------------
-- Returns string representation "<num>/<denom>"
-- @return {string}
----------------------------------------
on toString (me)
  return me.num&"/"&me.denom
end

----------------------------------------
--
----------------------------------------
on _gcd (me, a, b)
  if a = 0 then return b
  if b = 0 then return a
  if a > b then return me._gcd(b, a mod b)
  return me._gcd(a, b mod a)
end

Lingo does not support overwriting built-in operators, so 'frac'-operators must be implemented as functions:

-- Frac library (movie script)

----------------------------------------
-- Shortcut for creating 'frac' values
-- @param {integer} numerator
-- @param {integer} denominator
-- @return {instance}
----------------------------------------
on frac (numerator, denominator)
  return script("Frac").new(numerator, denominator)
end

----------------------------------------
-- All functions below this comment only support 'fracs', i.e. instances
-- of the Frac Class, as arguments. An integer n is casted to frac via frac(n).
----------------------------------------

-- Optionally supports more than 2 arguments
on fAdd (a, b) -- ...
  res = a
  repeat with i = 2 to the paramCount
    p = param(i)
    num = res.num * p.denom + res.denom * p.num
    denom = res.denom * p.denom
    res = frac(num, denom)
  end repeat
  return res
end

on fSub (a, b)
  return frac(a.num * b.den - a.den * b.num, a.den * b.den)
end

-- Optionally supports more than 2 arguments
on fMul (a, b) -- ...
  res = a
  repeat with i = 2 to the paramCount
    p = param(i)
    res = frac(res.num * p.num, res.denom * p.denom)
  end repeat
  return res
end

on fDiv (a, b)
  return frac(a.num * b.denom, a.denom * b.num)
end

on fAbs (f)
  return frac(abs(f.num), f.denom)
end

on fNeg (f)
  return frac(-f.num, f.denom)
end

on fEQ (a, b)
  diff = fSub(a, b)
  return diff.num=0
end

on fNE (a, b)
  return not fEQ (a, b)
end

on fGT (a, b)
  diff = fSub(a, b)
  return diff.num>0
end

on fLT (a, b)
  diff = fSub(a, b)
  return diff.num<0
end

on fGE (a, b)
  diff = fSub(a, b)
  return diff.num>=0
end

on fLE (a, b)
  diff = fSub(a, b)
  return diff.num<=0
end

Usage:

f = frac(2,3)
put f.toString()
-- "2/3"

-- fractions are normalized on the fly
f = frac(4,6)
put f.toString()
-- "2/3"

-- casting integer to frac
f = frac(23)
put f.toString()
-- "23/1"

Finding perfect numbers:

-- in some movie script
----------------------------------------
-- Prints all perfect numbers up to n
-- @param {integer|float} n
----------------------------------------
on findPerfects (n)
  repeat with i = 2 to n
    sum = frac(1, i)
    cnt = sqrt(i)
    repeat with fac = 2 to cnt
      if i mod fac = 0 then sum = fAdd(sum, frac(1, fac), frac(fac, i))
    end repeat
    if sum.denom = sum.num then put i
  end repeat
end
findPerfects(power(2, 19))
-- 6
-- 28
-- 496
-- 8128

Lua

function gcd(a,b) return a == 0 and b or gcd(b % a, a) end

do
  local function coerce(a, b)
    if type(a) == "number" then return rational(a, 1), b end
    if type(b) == "number" then return a, rational(b, 1) end
    return a, b
  end
  rational = setmetatable({
  __add = function(a, b)
      local a, b = coerce(a, b)
      return rational(a.num * b.den + a.den * b.num, a.den * b.den)
    end,
  __sub = function(a, b)
      local a, b = coerce(a, b)
      return rational(a.num * b.den - a.den * b.num, a.den * b.den)
    end,
  __mul = function(a, b)
      local a, b = coerce(a, b)
      return rational(a.num * b.num, a.den * b.den)
    end,
  __div = function(a, b)
      local a, b = coerce(a, b)
      return rational(a.num * b.den, a.den * b.num)
    end,
  __pow = function(a, b)
      if type(a) == "number" then return a ^ (b.num / b.den) end
      return rational(a.num ^ b, a.den ^ b) --runs into a problem if these aren't integers
    end,
  __concat = function(a, b)
      if getmetatable(a) == rational then return a.num .. "/" .. a.den .. b end
      return a .. b.num .. "/" .. b.den
    end,
  __unm = function(a) return rational(-a.num, -a.den) end}, {
  __call = function(z, a, b) return setmetatable({num = a / gcd(a, b),den = b / gcd(a, b)}, z) end} )
end

print(rational(2, 3) + rational(3, 5) - rational(1, 10) .. "") --> 7/6
print((rational(4, 5) * rational(5, 9)) ^ rational(1, 2) .. "") --> 2/3
print(rational(45, 60) / rational(5, 2) .. "") --> 3/10
print(5 + rational(1, 3) .. "") --> 16/3

function findperfs(n)
  local ret = {}
  for i = 1, n do
    sum = rational(1, i)
    for fac = 2, i^.5 do
      if i % fac == 0 then
        sum = sum + rational(1, fac) + rational(fac, i)
      end
    end
    if sum.den == sum.num then
      ret[#ret + 1] = i
    end
  end
  return table.concat(ret, '\n')
end
print(findperfs(2^19))

M2000 Interpreter

http://www.rosettacode.org/wiki/M2000_Interpreter_rational_numbers

Class Rational {
      \\ this is a compact version for this task
      numerator as decimal, denominator as decimal
      gcd=lambda->0
      lcm=lambda->0
      operator "+" {
           Read l
           denom=.lcm(l.denominator, .denominator)
           .numerator<=denom/l.denominator*l.numerator+denom/.denominator*.numerator
           if .numerator==0 then denom=1
           .denominator<=denom
      }
      Group Real {
            value {
                  link parent numerator, denominator to n, d
                  =n/d
            }
      }
      Group ToString$ {
           value {
                  link parent numerator, denominator to n, d
                  =Str$(n)+"/"+Str$(d,"")
            }      
      }
      class:
            Module Rational (.numerator, .denominator) {
                  if .denominator=0 then Error "Zero denominator"
                  sgn=Sgn(.numerator)*Sgn(.denominator)
                  .denominator<=abs(.denominator)
                  .numerator<=abs(.numerator)*sgn
                  gcd1=lambda (a as decimal, b as decimal) -> {
                        if a<b then swap a,b
                        g=a mod b
                        while g {
                              a=b:b=g: g=a mod b
                        }
                        =abs(b)
                  }
                  gdcval=gcd1(abs(.numerator), .denominator)
                  if gdcval<.denominator and gdcval<>0 then
                        .denominator/=gdcval
                        .numerator/=gdcval
                  end if
                  .gcd<=gcd1
                  .lcm<=lambda gcd=gcd1 (a as decimal, b as decimal) -> {
                        =a/gcd(a,b)*b
                  }
            }
}
sum=rational(1, 1)
onediv=rational(1,1)
divcand=rational(1,1)
Profiler
For sum.denominator= 2 to 2**15 {
      divcand.denominator=sum.denominator
      For onediv.denominator=2 to sqrt(sum.denominator) {
            if sum.denominator mod onediv.denominator = 0 then {
                  divcand.numerator=onediv.denominator
                  sum=sum+onediv+divcand
            }
      }
      if sum.real=1 then Print sum.denominator;" is perfect"
      sum.numerator=1
}
Print timecount

Maple

Maple has full built-in support for arithmetic with fractions (rational numbers). Fractions are treated like any other number in Maple.

> a := 3 / 5;
                                a := 3/5

> numer( a );
                                   3

> denom( a );
                                   5

However, while you can enter a fraction such as "4/6", it will automatically be reduced so that the numerator and denominator have no common factor:

> b := 4 / 6;
                                b := 2/3

All the standard arithmetic operators work with rational numbers. It is not necessary to call any special routines.

> a + b;
                                   19
                                   --
                                   15

> a * b;
                                  2/5

> a / b;
                                  9/10

> a - b;
                                   -1
                                   --
                                   15

> a + 1;
                                  8/5

> a - 1;
                                  -2/5

Notice that fractions are treated as exact quantities; they are not converted to floats. However, you can get a floating point approximation to any desired accuracy by applying the function evalf to a fraction.

> evalf( 22 / 7 ); # default is 10 digits
                              3.142857143

> evalf[100]( 22 / 7 ); # 100 digits
3.142857142857142857142857142857142857142857142857142857142857142857\
    142857142857142857142857142857143

Mathematica / Wolfram Language

Mathematica has full support for fractions built-in. If one divides two exact numbers it will be left as a fraction if it can't be simplified. Comparison, addition, division, product et cetera are built-in:

4/16
3/8  
8/4
4Pi/2
16!/10!
Sqrt[9/16]
Sqrt[3/4]
(23/12)^5
2 + 1/(1 + 1/(3 + 1/4))

1/2+1/3+1/5
8/Pi+Pi/8 //Together
13/17 + 7/31
Sum[1/n,{n,1,100}]      (*summation of 1/1 + 1/2 + 1/3 + 1/4+ .........+ 1/99 + 1/100*)

1/2-1/3
a=1/3;a+=1/7

1/4==2/8
1/4>3/8
Pi/E >23/20
1/3!=123/370
Sin[3]/Sin[2]>3/20

Numerator[6/9]
Denominator[6/9]

gives back:

1/4
3/8
2
2 Pi
5765760
3/4
Sqrt[3]/2
6436343 / 248832
47/17

31/30
(64+Pi^2) / (8 Pi)
522 / 527
14466636279520351160221518043104131447711 / 2788815009188499086581352357412492142272

1/6
10/21

True
False
True
True
True

2
3

As you can see, Mathematica automatically handles fraction as exact things, it doesn't evaluate the fractions to a float. It only does this when either the numerator or the denominator is not exact. I only showed integers above, but Mathematica can handle symbolic fraction in the same and complete way:

c/(2 c)
(b^2 - c^2)/(b - c)  // Cancel
1/2 + b/c // Together

gives back:

1/2
b+c
(2 b+c) / (2 c)

Moreover it does simplification like Sin[x]/Cos[x] => Tan[x]. Division, addition, subtraction, powering and multiplication of a list (of any dimension) is automatically threaded over the elements:

1+2*{1,2,3}^3

gives back:

{3, 17, 55}

To check for perfect numbers in the range 1 to 2^25 we can use:

found={};
CheckPerfect[num_Integer]:=If[Total[1/Divisors[num]]==2,AppendTo[found,num]];
Do[CheckPerfect[i],{i,1,2^25}];
found

gives back:

{6, 28, 496, 8128, 33550336}

Final note; approximations of fractions to any precision can be found using the function N.

Maxima

/* Rational numbers are builtin */
a: 3 / 11;
3/11

b: 117 / 17;
117/17

a + b;
1338/187

a - b;
-1236/187

a * b;
351/187

a / b;
17/429

a^5;
243/161051

num(a);
3

denom(a);
11

ratnump(a);
true

Modula-2

[This section is included from a subpage and should be edited there, not here.]
Works with: FST Modula-2 v4.0 version no object oriented code used

This is incomplete as the Perfect Numbers task has not been addressed.

Definition Module

<lang modula2>DEFINITION MODULE Rational;

   TYPE RAT =  RECORD
                   numerator : INTEGER;
                   denominator : INTEGER;
               END;
   PROCEDURE IGCD( i : INTEGER; j : INTEGER ) : INTEGER;
   PROCEDURE ILCM( i : INTEGER; j : INTEGER ) : INTEGER;
   PROCEDURE IABS( i : INTEGER ) : INTEGER;
   PROCEDURE RNormalize( i : RAT ) : RAT;
   PROCEDURE RCreate( num : INTEGER; dem : INTEGER ) : RAT;
   PROCEDURE RAdd( i : RAT; j : RAT ) : RAT;
   PROCEDURE RSubtract( i : RAT; j : RAT ) : RAT;
   PROCEDURE RMultiply( i : RAT; j : RAT ) : RAT;
   PROCEDURE RDivide( i : RAT; j : RAT ) : RAT;
   PROCEDURE RAbs( i : RAT ) : RAT;
   PROCEDURE RInv( i : RAT ) : RAT;
   PROCEDURE RNeg( i : RAT ) : RAT;
   PROCEDURE RInc( i : RAT ) : RAT;
   PROCEDURE RDec( i : RAT ) : RAT;
   
   PROCEDURE REQ( i : RAT; j : RAT ) : BOOLEAN;
   PROCEDURE RNE( i : RAT; j : RAT ) : BOOLEAN;
   PROCEDURE RLT( i : RAT; j : RAT ) : BOOLEAN;
   PROCEDURE RLE( i : RAT; j : RAT ) : BOOLEAN;
   PROCEDURE RGT( i : RAT; j : RAT ) : BOOLEAN;
   PROCEDURE RGE( i : RAT; j : RAT ) : BOOLEAN;
   PROCEDURE RIsZero( i : RAT ) : BOOLEAN;
   PROCEDURE RIsNegative( i : RAT ) : BOOLEAN;
   PROCEDURE RIsPositive( i : RAT ) : BOOLEAN;
   PROCEDURE RToString( i : RAT; VAR S : ARRAY OF CHAR );
   PROCEDURE RToRational( s : ARRAY OF CHAR ) : RAT;
   PROCEDURE WriteRational( i : RAT );

END Rational.</lang>

Implementation Module

<lang modula2>IMPLEMENTATION MODULE Rational;

   FROM Strings IMPORT Assign, Append, Pos, Copy, Length;
   FROM NumberConversion IMPORT IntToString, StringToInt;
   FROM InOut IMPORT WriteString (*, WriteCard,WriteLine, WriteInt, WriteLn *);
   PROCEDURE IGCD( i : INTEGER; j : INTEGER ) : INTEGER;
   VAR
       res : INTEGER;
   BEGIN
       IF j = 0 THEN
           res := i;
       ELSE
           res := IGCD( j, i MOD j );
       END;
       RETURN res;
   END IGCD;
   PROCEDURE ILCM( i : INTEGER; j : INTEGER ) : INTEGER;
   VAR
       res : INTEGER;
   BEGIN
       res := (i DIV IGCD( i, j ) ) * j;
       RETURN res;
   END ILCM;
   PROCEDURE IABS( i : INTEGER ) : INTEGER;
   VAR
       res : INTEGER;
   BEGIN
       IF i < 0 THEN
           res := i * (-1);
       ELSE
           res := i;
       END;
       RETURN res;
   END IABS;
   PROCEDURE RNormalize( i : RAT ) : RAT;
   VAR
       gcd : INTEGER;
       res : RAT;
   BEGIN
       gcd := IGCD( ABS( i.numerator ), ABS( i.denominator ) );
       IF gcd <> 0 THEN
           res.numerator := i.numerator DIV gcd;
           res.denominator := i.denominator DIV gcd;
           IF ( res.denominator < 0 ) THEN
               res.numerator := res.numerator * (-1);
               res.denominator := res.denominator * (-1);
           END;
       ELSE
           WITH res DO
               numerator := 0;
               denominator := 0;
           END;
       END;
       RETURN res;
   END RNormalize;
   PROCEDURE RCreate( num : INTEGER; dem : INTEGER ) : RAT;
   VAR
       rat : RAT;
   BEGIN
       WITH rat DO
           numerator := num;
           denominator := dem;
       END;
       RETURN RNormalize(rat);
   END RCreate;
   PROCEDURE RAdd( i : RAT; j : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator * j.denominator + j.numerator * i.denominator, i.denominator * j.denominator );
   END RAdd;
   PROCEDURE RSubtract( i : RAT; j : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator * j.denominator - j.numerator * i.denominator, i.denominator * j.denominator );
   END RSubtract;
   PROCEDURE RMultiply( i : RAT; j : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator * j.numerator, i.denominator * j.denominator );
   END RMultiply;
   PROCEDURE RDivide( i : RAT; j : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator * j.denominator, i.denominator * j.numerator );
   END RDivide;
   PROCEDURE RAbs( i : RAT ) : RAT;
   BEGIN
       RETURN RCreate( IABS( i.numerator ), i.denominator );
   END RAbs;
   PROCEDURE RInv( i : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.denominator, i.numerator );
   END RInv;
   PROCEDURE RNeg( i : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator * (-1), i.denominator );
   END RNeg;
   PROCEDURE RInc( i : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator + i.denominator, i.denominator );
   END RInc;
   PROCEDURE RDec( i : RAT ) : RAT;
   BEGIN
       RETURN RCreate( i.numerator - i.denominator, i.denominator );
   END RDec;
   PROCEDURE REQ( i : RAT; j : RAT ) : BOOLEAN;
   VAR
       ii : RAT;
       jj : RAT;
   BEGIN
       ii := RNormalize( i );
       jj := RNormalize( j );
       RETURN ( ( ii.numerator = jj.numerator ) AND ( ii.denominator = jj.denominator ) );
   END REQ;
   PROCEDURE RNE( i : RAT; j : RAT ) : BOOLEAN;
   BEGIN
       RETURN NOT REQ( i, j );
   END RNE;
   PROCEDURE RLT( i : RAT; j : RAT ) : BOOLEAN;
   BEGIN
       RETURN RIsNegative( RSubtract( i, j ) );
   END RLT;
   PROCEDURE RLE( i : RAT; j : RAT ) : BOOLEAN;
   BEGIN
       RETURN NOT RGT( i, j );
   END RLE;
   PROCEDURE RGT( i : RAT; j : RAT ) : BOOLEAN;
   BEGIN
       RETURN RIsPositive( RSubtract( i, j ) );
   END RGT;
   PROCEDURE RGE( i : RAT; j : RAT ) : BOOLEAN;
   BEGIN
       RETURN NOT RLT( i, j );
   END RGE;
   PROCEDURE RIsZero( i : RAT ) : BOOLEAN;
   BEGIN
       RETURN i.numerator = 0;
   END RIsZero;
   PROCEDURE RIsNegative( i : RAT ) : BOOLEAN;
   BEGIN
       RETURN i.numerator < 0;
   END RIsNegative;
   PROCEDURE RIsPositive( i : RAT ) : BOOLEAN;
   BEGIN
       RETURN i.numerator > 0;
   END RIsPositive;
   PROCEDURE RToString( i : RAT; VAR S : ARRAY OF CHAR );
   VAR
       num : ARRAY [1..15] OF CHAR;
       den : ARRAY [1..15] OF CHAR;
   BEGIN
       IF RIsZero( i ) THEN
           Assign("0", S );
       ELSE
           IntToString( i.numerator, num, 1 );
           Assign( num, S );
           IF ( i.denominator <> 1 ) THEN
               IntToString( i.denominator, den, 1 );
               Append( S, "/" );
               Append( S, den );
           END;
       END;
   END RToString;
   PROCEDURE RToRational( s : ARRAY OF CHAR ) : RAT;
   VAR
       n : CARDINAL;
       numer : INTEGER;
       denom : INTEGER;
       LHS, RHS : ARRAY [ 1..20 ] OF CHAR;
       Flag : BOOLEAN;
   BEGIN
       numer := 0;
       denom := 0;
       n := Pos( "/", s );
       IF n > HIGH( s ) THEN
           StringToInt( s, numer, Flag );
           IF Flag THEN
               denom := 1;
           END;
       ELSE
           Copy( s, 0, n, LHS );
           Copy( s, n+1, Length( s ), RHS );
           StringToInt( LHS, numer, Flag );
           IF Flag THEN
               StringToInt( RHS, denom, Flag );
           END;
       END;
       RETURN RCreate( numer, denom );
   END RToRational;
   PROCEDURE WriteRational( i : RAT );
   VAR
       res : ARRAY [0 .. 80] OF CHAR;
   BEGIN
       RToString( i, res );
       WriteString( res );
   END WriteRational;

END Rational.</lang>

Test Program

<lang modula2>MODULE TestRat;

      FROM InOut IMPORT WriteString, WriteLine;
      FROM Terminal IMPORT KeyPressed;
      FROM Strings IMPORT CompareStr;
      FROM Rational IMPORT RAT, IGCD, RCreate, RToString, RIsZero, RNormalize,
                           RToRational, REQ, RNE, RLT, RLE, RGT, RGE, WriteRational,
                           RAdd, RSubtract, RMultiply, RDivide, RAbs, RNeg, RInv;

VAR

   res : INTEGER;
   a, b, c, d, e, f : RAT;
   ans : ARRAY [1..100] OF CHAR;

PROCEDURE Assert( F : BOOLEAN; S : ARRAY OF CHAR ); BEGIN

   IF ( NOT F) THEN
       WriteLine( S );
   END;

END Assert;

BEGIN

   a := RCreate( 0, 0 );
   Assert( RIsZero( a ), "RIsZero( a )");
   a := RToRational("2");
   RToString( a, ans );
   res := CompareStr( ans, "2" );
   Assert( (res = 0), "CompareStr( RToString( a ), '2' ) = 0");
   a := RToRational("1/2");
   RToString( a, ans );
   res := CompareStr( ans, "1/2");
   Assert( res = 0, "CompareStr( RToString( a, ans ), '1/2') = 0");
   b := RToRational( "2/-12" );
   RToString( b, ans );
   res := CompareStr( ans, "-1/6");
   Assert( res = 0, "CompareStr( RToString( b, ans ), '-1/6') = 0");
   f := RCreate( 0, 9 ); (* rationalizes internally to zero *)
   a := RToRational("1/3");
   b := RToRational("1/2");
   c := RCreate( 1, 3 );
   Assert( NOT REQ( a, b ), "1/3 == 1/2" );
   Assert( REQ( a, c ), "1/3 == 1/3" );
   Assert( RNE( a, b ), "1/3 != 1/2" );
   Assert( RLT( a, b ), "1/3 < 1/2" );
   Assert( NOT RLT(b,a), "1/2 < 1/3" );
   Assert( NOT RLT(a,c), "1/3 < 1/3" );
   Assert( NOT RGT(a,b), "1/3 > 1/2" );
   Assert( RGT(b,a), "1/2 > 1/3" );
   Assert( NOT RGT(a,c), "1/3 > 1/3" );
   Assert( RLE( a, b ), "1/3 <= 1/2" );
   Assert( NOT RLE( b, a ), "1/2 <= 1/3" );
   Assert( RLE( a, c ), "1/3 <= 1/3" );
   Assert( NOT RGE(a,b), "1/3 >= 1/2" );
   Assert( RGE(b,a), "1/2 >= 1/3" );
   Assert( RGE( a,c ), "1/3 >= 1/3" );
   a := RCreate(1,2);
   b := RCreate(1,6);
   a := RAdd( a, b );
   Assert( REQ( a, RToRational("2/3")), "1/2 + 1/6 == 2/3" );
   c := RNeg( a );
   Assert( REQ( a, RCreate( 2,3)), "2/3 == 2/3" );
   Assert( REQ( c, RCreate( 2,-3)), "Neg 1/2 == -1/2" );
   a := RCreate( 2,-3);
   d := RAbs( c );
   Assert( REQ( d, RCreate( 2,3 ) ), "abs(neg(1/2))==1/2" );
   a := RToRational( "1/2");
   b := RSubtract( b, a );
   Assert( REQ( b, RCreate(-1,3) ), "1/6 - 1/2 == -1/3" );
   c := RInv(b);
   RToString( c, ans );
   res := CompareStr( ans, "-3" );
   Assert( res = 0, "inv(1/6 - 1/2) == -3" );
   f := RInv( f ); (* as f normalized to zero, the reciprocal is still zero *)


   b := RCreate( 1, 6);
   b := RAdd( b, RAdd( RCreate( 2,3), RCreate( 4,2 )));
   RToString( b, ans );
   res := CompareStr( ans, "17/6" );
   Assert( res = 0, "1/6 + 2/3 + 4/2 = 17/6" );
   a := RCreate(1,3);
   b := RCreate(1,6);
   c := RCreate(5,6);
   d := RToRational("1/5");
   e := RToRational("2");
   f := RToRational("0/9");
   Assert( REQ( RMultiply( c, d ), b ), "5/6 * 1/5 = 1/6" );
   Assert( REQ( RMultiply( c, RMultiply( d, e ) ), a ), "5/6 * 1/5 * 2 = 1/3" );
   Assert( REQ( RMultiply( c, RMultiply( d, RMultiply( e, f ) ) ), f ), "5/6 * 1/5 * 2 * 0" );
   Assert( REQ( b, RDivide( c, RToRational("5" ) ) ), "5/6 / 5 = 1/6" );
   e := RDivide( c, f ); (* RDivide multiplies so no divide by zero *)
   WriteString("Press any key..."); WHILE NOT KeyPressed() DO END;

END TestRat.</lang>

Modula-3

[This section is included from a subpage and should be edited there, not here.]

Template:Rational Arithmetic

This implementation implements the task in a module named Frac that exports an opaque type named T, a standard Modula-3 practice. Although the task does not require it, the module also defines the exception ZeroDenominator for occasions when one might attempt division by zero, which includes at initialization. It provides two functions that return rational numbers for 0 and 1.

Unfortunately IMHO Modula-3 eschewed operator overloading, so the arithmetic looks unpleasant.

Interface module

<lang modula3>INTERFACE Frac;

EXCEPTION ZeroDenominator;

TYPE

 T <: Public;
 Public = OBJECT
 METHODS
   (* initialization and conversion *)
   init(a, b: INTEGER): T RAISES { ZeroDenominator };
   fromInt(a: INTEGER): T;
   (* unary operators *)
   abs(): T;
   opposite(): T;
   (* binary operators *)
   plus(other: T): T;
   minus(other: T): T;
   times(other: T): T;
   dividedBy(other: T): T RAISES { ZeroDenominator };
   integerDivision(other: INTEGER): T RAISES { ZeroDenominator };
   (* relations *)
   equals(other: T): BOOLEAN;
   notEqualTo(other: T): BOOLEAN;
   lessThan(other: T): BOOLEAN;
   lessEqual(other: T): BOOLEAN;
   greaterEqual(other: T): BOOLEAN;
   greater(other: T): BOOLEAN;
   (* other easily-checked properties *)
   isInt(): BOOLEAN;
   (* inc/decrement *)
   inc(step: CARDINAL := 1);
   dec(step: CARDINAL := 1);
 END;
 PROCEDURE One():  T;
 PROCEDURE Zero(): T;
 (* I/O *)
 PROCEDURE PutRat(a: T);

END Frac.</lang>

Implementation module

The implementation keeps all rational numbers in simplest form.

<lang modula3>MODULE Frac;

IMPORT IO;

TYPE

 REVEAL T = Public BRANDED OBJECT
   num: INTEGER := 0;
   den: INTEGER := 1;
 OVERRIDES
   init := Init;
   fromInt := FromInt;
   abs := Abs;
   opposite := Opposite;
   plus := Plus;
   minus := Minus;
   times := Times;
   dividedBy := DividedBy;
   integerDivision := IntegerDivision;
   equals := Equals;
   notEqualTo := NotEqualTo;
   lessThan := LessThan;
   lessEqual := LessEqual;
   greaterEqual := GreaterEqual;
   greater := Greater;
   isInt := IsInt;
   inc := Inc;
   dec := Dec;
 END;

PROCEDURE Gcd(a, b: CARDINAL): CARDINAL = VAR

 m := MAX(a, b);
 n := MIN(a, b);
 r: CARDINAL;

BEGIN

 WHILE n # 0 DO
   r := m MOD n;
   m := n;
   n := r;
 END;
 RETURN m;

END Gcd;

PROCEDURE Simplify(VAR a: T) = VAR

 d := Gcd(ABS(a.num), ABS(a.den));

BEGIN

 a.num := a.num DIV d;
 a.den := a.den DIV d;

END Simplify;

PROCEDURE Init(self: T; a, b: INTEGER): T RAISES { ZeroDenominator } = BEGIN

 IF (b > 0) THEN
   self.num := a;
   self.den := b;
 ELSIF (b < 0) THEN
   self.num := -a;
   self.den := -b;
 ELSE
   RAISE ZeroDenominator;
 END;
 Simplify(self);
 RETURN self;

END Init;

PROCEDURE FromInt(self: T; a: INTEGER): T = BEGIN

 self.num := a;
 self.den := 1;
 RETURN self;

END FromInt;

PROCEDURE Abs(self: T): T = BEGIN

 RETURN NEW(T, num := ABS(self.num), den := self.den);

END Abs;

PROCEDURE Opposite(self: T): T = BEGIN

 RETURN NEW(T, num := -self.num, den := self.den);

END Opposite;

PROCEDURE Plus(self, other: T): T = VAR

 result := NEW( T,
     num := self.num * other.den + self.den * other.num,
     den := self.den * other.den
 );

BEGIN

 Simplify(result);
 RETURN result;

END Plus;

PROCEDURE Minus(self, other: T): T = VAR

 result := NEW( T,
     num := self.num * other.den - self.den * other.num,
     den := self.den * other.den
 );

BEGIN

 Simplify(result);
 RETURN result;

END Minus;

PROCEDURE Times(self, other: T): T = VAR

 result := NEW( T,
     num := self.num * other.num,
     den := self.den * other.den
 );

BEGIN

 Simplify(result);
 RETURN result;

END Times;

PROCEDURE DividedBy(self, other: T): T RAISES { ZeroDenominator } = VAR

 result := NEW( T,
     num := self.num * other.den,
     den := self.den * other.num
 );

BEGIN

 IF result.den = 0 THEN RAISE ZeroDenominator; END;
 IF result.den < 0 THEN
   result.num := -result.num;
   result.den := -result.den;
 END;
 Simplify(result);
 RETURN result;

END DividedBy;

PROCEDURE IntegerDivision(self: T; other: INTEGER): T RAISES { ZeroDenominator } = VAR

 result := NEW( T, num := self.num, den := self.den * other );

BEGIN

 IF other = 0 THEN RAISE ZeroDenominator; END;
 Simplify(result);
 RETURN result;

END IntegerDivision;

PROCEDURE Equals(self, other: T): BOOLEAN = BEGIN

 (* this trick works only because we simplify after each operation *)
 RETURN (self.num = other.num) AND (self.den = other.den);

END Equals;

PROCEDURE NotEqualTo(self, other: T): BOOLEAN = BEGIN

 (* this trick works only because we simplify after each operation *)
 RETURN (self.num # other.num) OR (self.den # other.den);

END NotEqualTo;

PROCEDURE LessThan(self, other: T): BOOLEAN = BEGIN

 RETURN self.num * other.den < self.den * other.num;

END LessThan;

PROCEDURE LessEqual(self, other: T): BOOLEAN = BEGIN

 RETURN self.num * other.den <= self.den * other.num;

END LessEqual;

PROCEDURE GreaterEqual(self, other: T): BOOLEAN = BEGIN

 RETURN self.num * other.den >= self.den * other.num;

END GreaterEqual;

PROCEDURE Greater(self, other: T): BOOLEAN = BEGIN

 RETURN self.num * other.den > self.den * other.num;

END Greater;

PROCEDURE Inc(self: T; step: CARDINAL) = BEGIN

 INC(self.num, step * self.den);

END Inc;

PROCEDURE Dec(self: T; step: CARDINAL) = BEGIN

 DEC(self.num, step * self.den);

END Dec;

PROCEDURE IsInt(self: T): BOOLEAN = BEGIN

 RETURN self.den = 1;

END IsInt;

PROCEDURE One(): T = BEGIN

 TRY
   RETURN NEW(T).init(1, 1);
 EXCEPT ZeroDenominator =>
   IO.Put("Something went very wrong.\n");
   RETURN NIL;
 END;

END One;

PROCEDURE Zero(): T = BEGIN

 TRY
   RETURN NEW(T).init(0, 1);
 EXCEPT ZeroDenominator =>
   IO.Put("Something went very wrong.\n");
   RETURN NIL;
 END;

END Zero;

PROCEDURE PutRat(a: T) = BEGIN

 IO.PutInt(a.num);
 IF a.den # 1 THEN
   IO.Put(" / "); IO.PutInt(a.den);
 END;

END PutRat;

BEGIN END Frac.</lang>

Test Program
Translation of: Nim

This module performs a few additional tests. The test for perfect numbers is based on that of Nim above. <lang modula3>MODULE TestRational EXPORTS Main;

IMPORT IO, Frac AS R, Word;

FROM Math IMPORT sqrt;

PROCEDURE PutBool(b: BOOLEAN) = BEGIN

 IF b THEN IO.Put("true");
 ELSE IO.Put("false");
 END;

END PutBool;

VAR

 a, b: R.T;
 n: Word.T := 2;
 limit: Word.T := 1;

BEGIN

 TRY
   a := NEW(R.T).init(2,3);
   b := NEW(R.T).init(-3,4);
 EXCEPT | R.ZeroDenominator =>
   IO.Put("Zero division!\n");
 END;
 R.PutRat(a); IO.Put(" op "); R.PutRat(b); IO.Put(" = \n");
 IO.Put("  + : "); R.PutRat(a.plus(b));  IO.PutChar('\n');
 IO.Put("  - : "); R.PutRat(a.minus(b)); IO.PutChar('\n');
 IO.Put("  * : "); R.PutRat(a.times(b)); IO.PutChar('\n');
 TRY
   IO.Put("  /: "); R.PutRat(a.dividedBy(b)); IO.PutChar('\n');
 EXCEPT | R.ZeroDenominator =>
   IO.Put("Zero division\n");
 END;
 IO.Put("  <  : "); PutBool(a.lessThan(b));     IO.PutChar('\n');
 IO.Put("  <= : "); PutBool(a.lessEqual(b));    IO.PutChar('\n');
 IO.Put("  >= : "); PutBool(a.greaterEqual(b)); IO.PutChar('\n');
 IO.Put("  >  : "); PutBool(a.greater(b));      IO.PutChar('\n');
 IO.PutChar('\n');
 IO.Put("Increasing "); R.PutRat(a); IO.Put(" by\n");
 IO.Put("  1 gives "); a.inc(1); R.PutRat(a); IO.PutChar('\n');
 IO.Put("  3 gives "); a.inc(3); R.PutRat(a); IO.PutChar('\n');
 IO.Put("Decreasing "); R.PutRat(a); IO.Put(" by\n");
 IO.Put("  1 gives "); a.dec(1); R.PutRat(a); IO.PutChar('\n');
 IO.Put("  3 gives "); a.dec(3); R.PutRat(a); IO.PutChar('\n');
 limit := Word.LeftShift(limit, 19);
 TRY
   WHILE n < limit DO
       VAR
         sum := NEW(R.T).init(1, n);
         maxFac := FLOOR(sqrt(FLOAT(n, LONGREAL)));
       BEGIN
         FOR i := 2 TO maxFac DO
           IF n MOD i = 0 THEN
             sum := sum.plus(NEW(R.T).init(1, i))
                       .plus(NEW(R.T).init(1, n DIV i));
           END;
         END;
         IF sum.isInt() THEN
           IO.Put("sum of reciprocal factors of "); IO.PutInt(n);
           IO.Put(" is exactly "); R.PutRat(sum);
           IF sum.equals(R.One()) THEN
             IO.Put(" perfect!");
           END;
           IO.PutChar('\n');
         END;
       END;
     INC(n, 2);
   END;
 EXCEPT R.ZeroDenominator =>
   IO.Put("Something went very wrong\n");
 END;

END TestRational.</lang>

Output:
2 / 3 op -3 / 4 = 
  + : -1 / 12
  - : 17 / 12
  * : -1 / 2
  /: -8 / 9
  <  : false
  <= : false
  >= : true
  >  : true

Increasing 2 / 3 by
  1 gives 5 / 3
  3 gives 14 / 3
Decreasing 14 / 3 by
  1 gives 11 / 3
  3 gives 2 / 3
sum of reciprocal factors of 6 is exactly 1 perfect!
sum of reciprocal factors of 28 is exactly 1 perfect!
sum of reciprocal factors of 120 is exactly 2
sum of reciprocal factors of 496 is exactly 1 perfect!
sum of reciprocal factors of 672 is exactly 2
sum of reciprocal factors of 8128 is exactly 1 perfect!
sum of reciprocal factors of 30240 is exactly 3
sum of reciprocal factors of 32760 is exactly 3
sum of reciprocal factors of 523776 is exactly 2

Nim

import math

proc `^`[T](base, exp: T): T =
  var (base, exp) = (base, exp)
  result = 1

  while exp != 0:
    if (exp and 1) != 0:
      result *= base
    exp = exp shr 1
    base *= base

proc gcd[T](u, v: T): T =
  if v != 0:
    gcd(v, u mod v)
  else:
    u.abs

proc lcm[T](a, b: T): T =
  a div gcd(a, b) * b

type Rational* = tuple[num, den: int64]

proc fromInt*(x: SomeInteger): Rational =
  result.num = x
  result.den = 1

proc frac*(x: var Rational) =
  let common = gcd(x.num, x.den)
  x.num = x.num div common
  x.den = x.den div common

proc `+` *(x, y: Rational): Rational =
  let common = lcm(x.den, y.den)
  result.num = common div x.den * x.num + common div y.den * y.num
  result.den = common
  result.frac

proc `+=` *(x: var Rational, y: Rational) =
  let common = lcm(x.den, y.den)
  x.num = common div x.den * x.num + common div y.den * y.num
  x.den = common
  x.frac

proc `-` *(x: Rational): Rational =
  result.num = -x.num
  result.den = x.den

proc `-` *(x, y: Rational): Rational =
  x + -y

proc `-=` *(x: var Rational, y: Rational) =
  x += -y

proc `*` *(x, y: Rational): Rational =
  result.num = x.num * y.num
  result.den = x.den * y.den
  result.frac

proc `*=` *(x: var Rational, y: Rational) =
  x.num *= y.num
  x.den *= y.den
  x.frac

proc reciprocal*(x: Rational): Rational =
  result.num = x.den
  result.den = x.num

proc `div`*(x, y: Rational): Rational =
  x * y.reciprocal

proc toFloat*(x: Rational): float =
  x.num.float / x.den.float

proc toInt*(x: Rational): int64 =
  x.num div x.den

proc cmp*(x, y: Rational): int =
  cmp x.toFloat, y.toFloat

proc `<` *(x, y: Rational): bool =
  x.toFloat < y.toFloat

proc `<=` *(x, y: Rational): bool =
  x.toFloat <= y.toFloat

proc abs*(x: Rational): Rational =
  result.num = abs x.num
  result.den = abs x.den

for candidate in 2'i64 .. <((2'i64)^19):
  var sum: Rational = (1'i64, candidate)
  for factor in 2'i64 .. pow(candidate.float, 0.5).int64:
    if candidate mod factor == 0:
      sum += (1'i64, factor) + (1'i64, candidate div factor)
  if sum.den == 1:
    echo "Sum of recipr. factors of ",candidate," = ",sum.num," exactly ",
      if sum.num == 1: "perfect!" else: ""

Output:

Sum of recipr. factors of 6 = 1 exactly perfect!
Sum of recipr. factors of 28 = 1 exactly perfect!
Sum of recipr. factors of 120 = 2 exactly 
Sum of recipr. factors of 496 = 1 exactly perfect!
Sum of recipr. factors of 672 = 2 exactly 
Sum of recipr. factors of 8128 = 1 exactly perfect!
Sum of recipr. factors of 30240 = 3 exactly 
Sum of recipr. factors of 32760 = 3 exactly 
Sum of recipr. factors of 523776 = 2 exactly 

Objective-C

See Arithmetic/Rational/Objective-C.

OCaml

OCaml's Num library implements arbitrary-precision rational numbers:

#load "nums.cma";;
open Num;;

for candidate = 2 to 1 lsl 19 do
  let sum = ref (num_of_int 1 // num_of_int candidate) in
  for factor = 2 to truncate (sqrt (float candidate)) do
    if candidate mod factor = 0 then
      sum := !sum +/ num_of_int 1 // num_of_int factor
                  +/ num_of_int 1 // num_of_int (candidate / factor)
  done;
  if is_integer_num !sum then
    Printf.printf "Sum of recipr. factors of %d = %d exactly %s\n%!"
        candidate (int_of_num !sum) (if int_of_num !sum = 1 then "perfect!" else "")
done;;

Delimited overloading can be used to make the arithmetic expressions more readable:

let () =
  for candidate = 2 to 1 lsl 19 do
    let sum = ref Num.(1 / of_int candidate) in
    for factor = 2 to truncate (sqrt (float candidate)) do
      if candidate mod factor = 0 then
        sum := Num.(!sum + 1 / of_int factor + of_int factor / of_int candidate)
    done;
    if Num.is_integer_num !sum then
      Printf.printf "Sum of recipr. factors of %d = %d exactly %s\n%!"
        candidate Num.(to_int !sum) (if Num.(!sum = 1) then "perfect!" else "")
  done

A type for rational numbers might be implemented like this:

First define the interface, hiding implementation details:

(* interface *)
module type RATIO =
   sig
     type t
     (* construct *)
     val frac : int -> int -> t
     val from_int : int -> t

     (* integer test *)
     val is_int : t -> bool

     (* output *)
     val to_string : t -> string

     (* arithmetic *)
     val cmp : t -> t -> int
     val ( +/ ) : t -> t -> t
     val ( -/ ) : t -> t -> t
     val ( */ ) : t -> t -> t
     val ( // ) : t -> t -> t
   end

then implement the module:

(* implementation conforming to signature *)
module Frac : RATIO =
   struct
      open Big_int

      type t = { num : big_int; den : big_int }
      
      (* short aliases for big_int values and functions *)
      let zero, one = zero_big_int, unit_big_int
      let big, to_int, eq = big_int_of_int, int_of_big_int, eq_big_int
      let (+~), (-~), ( *~) = add_big_int, sub_big_int, mult_big_int
      
      (* helper function *)
      let rec norm ({num=n;den=d} as k) =
         if lt_big_int d zero then
           norm {num=minus_big_int n;den=minus_big_int d}
         else
         let rec hcf a b =
           let q,r = quomod_big_int a b in
           if eq r zero then b else hcf b r in
         let f = hcf n d in
         if eq f one then k else
            let div = div_big_int in
            { num=div n f; den = div d f } (* inefficient *)
      
      (* public functions *)
      let frac a b = norm { num=big a; den=big b }
      
      let from_int a = norm { num=big a; den=one }

      let is_int {num=n; den=d} =
         eq d one ||
         eq (mod_big_int n d) zero
      
      let to_string ({num=n; den=d} as r) =
         let r1 = norm r in
         let str = string_of_big_int in
         if is_int r1 then
            str (r1.num)
         else
            str (r1.num) ^ "/" ^ str (r1.den)
      
      let cmp a b =
         let a1 = norm a and b1 = norm b in
         compare_big_int (a1.num*~b1.den) (b1.num*~a1.den)

      let ( */ ) {num=n1; den=d1} {num=n2; den=d2} =
         norm { num = n1*~n2; den = d1*~d2 }

      let ( // ) {num=n1; den=d1} {num=n2; den=d2} =
         norm { num = n1*~d2; den = d1*~n2 }
      
      let ( +/ ) {num=n1; den=d1} {num=n2; den=d2} =
         norm { num = n1*~d2 +~ n2*~d1; den = d1*~d2 }
      
      let ( -/ ) {num=n1; den=d1} {num=n2; den=d2} =
         norm { num = n1*~d2 -~ n2*~d1; den = d1*~d2 }
   end

Finally the use type defined by the module to perform the perfect number calculation:

(* use the module to calculate perfect numbers *)
let () =
   for i = 2 to 1 lsl 19 do
      let sum = ref (Frac.frac 1 i) in
      for factor = 2 to truncate (sqrt (float i)) do
         if i mod factor = 0 then
            Frac.(
            sum := !sum +/ frac 1 factor +/ frac 1 (i / factor)
            )
      done;
      if Frac.is_int !sum then
         Printf.printf "Sum of reciprocal factors of %d = %s exactly %s\n%!"
           i (Frac.to_string !sum) (if Frac.to_string !sum = "1" then "perfect!" else "")
   done

which produces this output:

Sum of reciprocal factors of 6 = 1 exactly perfect!
Sum of reciprocal factors of 28 = 1 exactly perfect!
Sum of reciprocal factors of 120 = 2 exactly
Sum of reciprocal factors of 496 = 1 exactly perfect!
Sum of reciprocal factors of 672 = 2 exactly
Sum of reciprocal factors of 8128 = 1 exactly perfect!
Sum of reciprocal factors of 30240 = 3 exactly
Sum of reciprocal factors of 32760 = 3 exactly
Sum of reciprocal factors of 523776 = 2 exactly

Ol

Otus Lisp has rational numbers built-in and integrated with all other number types.

(define x 3/7)
(define y 9/11)
(define z -2/5)

; demonstrate builtin functions:

(print "(abs " z ") = " (abs z))
(print "- " z " = " (- z))
(print x " + " y " = " (+ x y))
(print x " - " y " = " (- x y))
(print x " * " y " = " (* x y))
(print x " / " y " = " (/ x y))
(print x " < " y " = " (< x y))
(print x " > " y " = " (> x y))

; introduce new functions:

(define (+:= x) (+ x 1))
(define (-:= x) (- x 1))

(print "+:= " z " = " (+:= z))
(print "-:= " z " = " (-:= z))

; finally, find all perfect numbers less than 2^15:

(lfor-each (lambda (candidate)
      (let ((sum (lfold (lambda (sum factor)
                     (if (= 0 (modulo candidate factor))
                        (+ sum (/ 1 factor) (/ factor candidate))
                        sum))
                  (/ 1 candidate)
                  (liota 2 1 (+ (isqrt candidate) 1)))))
         (if (= 1 (denominator sum))
            (print candidate (if (eq? sum 1) ", perfect" "")))))
   (liota 2 1 (expt 2 15)))
Output:
(abs -2/5) = 2/5
- -2/5 = 2/5
3/7 + 9/11 = 96/77
3/7 - 9/11 = -30/77
3/7 * 9/11 = 27/77
3/7 / 9/11 = 11/21
3/7 < 9/11 = #true
3/7 > 9/11 = #false
+:= -2/5 = 3/5
-:= -2/5 = -7/5
6, perfect
28, perfect
120
496, perfect
672
8128, perfect
30240
32760

ooRexx

loop candidate = 6 to 2**19
    sum = .fraction~new(1, candidate)
    max2 = rxcalcsqrt(candidate)~trunc

    loop factor = 2 to max2
        if candidate // factor == 0 then do
           sum += .fraction~new(1, factor)
           sum += .fraction~new(1, candidate / factor)
        end
    end
    if sum == 1 then say candidate "is a perfect number"
end

::class fraction public inherit orderable
::method init
  expose numerator denominator
  use strict arg numerator, denominator = 1

  if denominator == 0 then raise syntax 98.900 array("Fraction denominator cannot be zero")

  -- if the denominator is negative, make the numerator carry the sign
  if denominator < 0 then do
      numerator = -numerator
      denominator = - denominator
  end


  -- find the greatest common denominator and reduce to
  -- the simplest form
  gcd = self~gcd(numerator~abs, denominator~abs)

  numerator /= gcd
  denominator /= gcd

-- fraction instances are immutable, so these are
-- read only attributes
::attribute numerator GET
::attribute denominator GET

-- calculate the greatest common denominator of a numerator/denominator pair
::method gcd private
  use arg x, y

  loop while y \= 0
      -- check if they divide evenly
      temp = x // y
      x = y
      y = temp
  end
  return x

-- calculate the least common multiple of a numerator/denominator pair
::method lcm private
  use arg x, y
  return x / self~gcd(x, y) * y

::method abs
  expose numerator denominator
  -- the denominator is always forced to be positive
  return self~class~new(numerator~abs, denominator)

::method reciprocal
  expose numerator denominator
  return self~class~new(denominator, numerator)

-- convert a fraction to regular Rexx number
::method toNumber
  expose numerator denominator

  if numerator == 0 then return 0
  return numerator/denominator

::method negative
  expose numerator denominator
  return self~class~new(-numerator, denominator)

::method add
  expose numerator denominator
  use strict arg other
  -- convert to a fraction if a regular number
  if \other~isa(.fraction) then other = self~class~new(other, 1)

  multiple = self~lcm(denominator, other~denominator)
  newa = numerator * multiple / denominator
  newb = other~numerator * multiple / other~denominator
  return self~class~new(newa + newb, multiple)

::method subtract
  use strict arg other
  return self + (-other)

::method times
  expose numerator denominator
  use strict arg other
  -- convert to a fraction if a regular number
  if \other~isa(.fraction) then other = self~class~new(other, 1)
  return self~class~new(numerator * other~numerator, denominator * other~denominator)

::method divide
  use strict arg other
  -- convert to a fraction if a regular number
  if \other~isa(.fraction) then other = self~class~new(other, 1)
  -- and multiply by the reciprocal
  return self * other~reciprocal

-- compareTo method used by the orderable interface to implement
-- the operator methods
::method compareTo
  expose numerator denominator
  -- convert to a fraction if a regular number
  if \other~isa(.fraction) then other = self~class~new(other, 1)

  return (numerator * other~denominator - denominator * other~numerator)~sign

-- we still override "==" and "\==" because we want to bypass the
-- checks for not being an instance of the class
::method "=="
  expose numerator denominator
  use strict arg other

  -- convert to a fraction if a regular number
  if \other~isa(.fraction) then other = self~class~new(other, 1)
  -- Note:  these are numeric comparisons, so we're using the "="
  -- method so those are handled correctly
  return numerator = other~numerator & denominator = other~denominator

::method "\=="
  use strict arg other
  return \self~"\=="(other)

-- some operator overrides -- these only work if the left-hand-side of the
-- subexpression is a quaternion
::method "*"
  forward message("TIMES")

::method "/"
  forward message("DIVIDE")

::method "-"
  -- need to check if this is a prefix minus or a subtract
  if arg() == 0 then
      forward message("NEGATIVE")
  else
      forward message("SUBTRACT")

::method "+"
  -- need to check if this is a prefix plus or an addition
  if arg() == 0 then
      return self  -- we can return this copy since it is imutable
  else
      forward message("ADD")

::method string
  expose numerator denominator
  if denominator == 1 then return numerator
  return numerator"/"denominator

-- override hashcode for collection class hash uses
::method hashCode
  expose numerator denominator
  return numerator~hashcode~bitxor(numerator~hashcode)

::requires rxmath library

Output:

6 is a perfect number
28 is a perfect number
496 is a perfect number
8128 is a perfect number

PARI/GP

Pari handles rational arithmetic natively.

for(n=2,1<<19,
  s=0;
  fordiv(n,d,s+=1/d);
  if(s==2,print(n))
)

Perl

Perl's Math::BigRat core module implements arbitrary-precision rational numbers. The bigrat pragma can be used to turn on transparent BigRat support:

use bigrat;

foreach my $candidate (2 .. 2**19) {
    my $sum = 1 / $candidate;
    foreach my $factor (2 .. sqrt($candidate)+1) {
        if ($candidate % $factor == 0) {
            $sum += 1 / $factor + 1 / ($candidate / $factor);
        }
    }
    if ($sum->denominator() == 1) {
        print "Sum of recipr. factors of $candidate = $sum exactly ", ($sum == 1 ? "perfect!" : ""), "\n";
    }
}

Phix

Translation of: Tcl

Phix does not support operator overloading (I am strongly opposed to such nonsense), nor does it have a native fraction library, but it might look a bit like this.

with javascript_semantics
without warning  -- (several unused routines in this code)
 
constant NUM = 1, DEN = 2
 
type frac(object r)
    return sequence(r) and length(r)=2 and integer(r[NUM]) and integer(r[DEN])
end type
 
function normalise(object n, atom d=0)
    if sequence(n) then
        {n,d} = n
    end if
    if d<0 then
        n = -n
        d = -d
    end if
    atom g = gcd(n,d)
    return {n/g,d/g}
end function
 
function frac_new(integer n, d=1)
    return normalise(n,d)
end function
 
function frac_abs(frac r)
    return {abs(r[NUM]),r[DEN]}
end function
 
function frac_inv(frac r)
    return reverse(r)
end function
 
function frac_add(frac a, b)
    integer {an,ad} = a,
            {bn,bd} = b
    return normalise(an*bd+bn*ad, ad*bd)
end function
 
function frac_sub(frac a, b)
    integer {an,ad} = a,
            {bn,bd} = b
    return normalise(an*bd-bn*ad, ad*bd)
end function
 
function frac_mul(frac a, b)
integer {an,ad} = a,
        {bn,bd} = b
    return normalise(an*bn, ad*bd)
end function
 
function frac_div(frac a, b)
integer {an,ad} = a,
        {bn,bd} = b
    return normalise(an*bd, ad*bn)
end function
 
function frac_eq(frac a, b)
    return a==b
end function
 
function frac_ne(frac a, b)
    return a!=b
end function
 
function frac_lt(frac a, b)
    return frac_sub(a,b)[NUM]<0
end function
 
function frac_gt(frac a, b)
    return frac_sub(a,b)[NUM]>0
end function
 
function frac_le(frac a, b)
    return frac_sub(a,b)[NUM]<=0
end function
 
function frac_ge(frac a, b)
    return frac_sub(a,b)[NUM]>=0
end function
 
function is_perfect(integer num)
    frac total = frac_new(0)
    sequence f = factors(num,1)
    for i=1 to length(f) do
        total = frac_add(total,frac_new(1,f[i]))
    end for
    return frac_eq(total,frac_new(2))
end function
 
procedure get_perfect_numbers()
    atom t0 = time()
    integer lim = power(2,iff(platform()=JS?13:19))
    for i=2 to lim do
        if is_perfect(i) then
            printf(1,"perfect: %d\n",i)
        end if
    end for
    printf(1,"elapsed: %3.2f seconds\n",time()-t0)
 
    integer pn5 = power(2,12)*(power(2,13)-1) -- 5th perfect number
    if is_perfect(pn5) then
        printf(1,"perfect: %d\n",pn5)
    end if
end procedure
 
get_perfect_numbers()
Output:
perfect: 6
perfect: 28
perfect: 496
perfect: 8128
elapsed: 13.56 seconds
perfect: 33550336

mpq

Library: Phix/mpfr

Turned out to be slightly slower than native, but worth it for large number support.
See also Bernoulli_numbers for another example of mpqs in action.

include builtins/mpfr.e
function is_perfect(integer num)
    mpq tot = mpq_init(),
        fth = mpq_init()
    sequence f = factors(num,1)
    for i=1 to length(f) do
        mpq_set_si(fth,1,f[i])
        mpq_add(tot,tot,fth)
    end for
    return mpq_cmp_si(tot,2,1)=0
end function
 
procedure get_perfect_numbers()
    atom t0 = time()
    integer lim = power(2,iff(platform()=JS?13:19))
    for i=2 to lim do
        if is_perfect(i) then
            printf(1,"perfect: %d\n",i)
        end if
    end for
    printf(1,"elapsed: %3.2f seconds\n",time()-t0)
 
    integer pn5 = power(2,12)*(power(2,13)-1) -- 5th perfect number
    if is_perfect(pn5) then
        printf(1,"perfect: %d\n",pn5)
    end if
end procedure
 
get_perfect_numbers()
Output:
perfect: 6
perfect: 28
perfect: 496
perfect: 8128
elapsed: 17.31 seconds
perfect: 33550336

Note that power(2,19) took over 270s under mpfr.js, so reduced to power(2,13) on that platform, making it finish in 0.99s

Picat

A naive addition algorithm is used, so the program is slow.

main =>
    foreach (I in 2..2**19, is_perfect(I))
        println(I)
    end.      
               
is_perfect(N) => sum_rationals([$frac(1,D) : D in divisors(N)]) == $frac(2,1).

divisors(N) = [I : I in 1..N, N mod I == 0].

add(frac(A,B), frac(C,D)) = new_fract(A*D+B*C, B*D).

new_fract(A,B) = $frac(Num, Den) =>
    G = gcd(A,B),
    Num = A // G,
    Den = B // G.

sum_rationals([X]) = X.
sum_rationals([X,Y|T]) = sum_rationals([add(X,Y)|T]).
Output:
6
28
496
8128

PicoLisp

(load "@lib/frac.l")

(for (N 2  (> (** 2 19) N)  (inc N))
   (let (Sum (frac 1 N)  Lim (sqrt N))
      (for (F 2  (>= Lim F) (inc F))
         (when (=0 (% N F))
            (setq Sum
               (f+ Sum
                  (f+ (frac 1 F) (frac 1 (/ N F))) ) ) ) )
      (when (= 1 (cdr Sum))
         (prinl
            "Perfect " N
            ", sum is " (car Sum)
            (and (= 1 (car Sum)) ": perfect") ) ) ) )
Output:
Perfect 6, sum is 1: perfect
Perfect 28, sum is 1: perfect
Perfect 120, sum is 2
Perfect 496, sum is 1: perfect
Perfect 672, sum is 2
Perfect 8128, sum is 1: perfect
Perfect 30240, sum is 3
Perfect 32760, sum is 3
Perfect 523776, sum is 2

PL/I

*process source attributes xref or(!);
 arat: Proc Options(main);
 /*--------------------------------------------------------------------
 * Rational Arithmetic
 * (Mis)use the Complex data type to represent fractions
 * real(x) is used as numerator
 * imag(x) is used as denominator
 * Output:
 * a=-3/7 b=9/2
 * a*b=-27/14
 * a+b=57/14
 * a-b=-69/14
 * a/b=-2/21
 * -3/7<9/2
 * 9/2>-3/7
 * -3/7=-3/7
 * 26.01.2015 handle 0/0
 *-------------------------------------------------------------------*/
 Dcl (abs,imag,mod,real,sign,trim) Builtin;
 Dcl sysprint Print;
 Dcl (candidate,max2,factor) Dec Fixed(15);
 Dcl sum complex Dec Fixed(15);
 Dcl one complex Dec Fixed(15);

 one=mk_fr(1,1);
 Put Edit('First solve the task at hand')(Skip,a);
 Do candidate = 2 to 10000;
   sum = mk_fr(1, candidate);
   max2 = sqrt(candidate);
   Do factor = 2 to max2;
     If mod(candidate,factor)=0 Then Do;
       sum=fr_add(sum,mk_fr(1,factor));
       sum=fr_add(sum,mk_fr(1,candidate/factor));
       End;
     End;
   If fr_cmp(sum,one)='=' Then Do;
     Put Edit(candidate,' is a perfect number')(Skip,f(7),a);
     Do factor = 2 to candidate-1;
       If mod(candidate,factor)=0 Then
         Put Edit(factor)(f(5));
       End;
     End;
   End;

 Put Edit('','Then try a few things')(Skip,a);
 Dcl a Complex Dec Fixed(15);
 Dcl b Complex Dec Fixed(15);
 Dcl p Complex Dec Fixed(15);
 Dcl s Complex Dec Fixed(15);
 Dcl d Complex Dec Fixed(15);
 Dcl q Complex Dec Fixed(15);
 Dcl zero Complex Dec Fixed(15);
 zero=mk_fr(0,1); Put Edit('zero=',fr_rep(zero))(Skip,2(a));
 a=mk_fr(0,0);    Put Edit('a=',fr_rep(a))(Skip,2(a));
 /*--------------------------------------------------------------------
 a=mk_fr(-3333,0); Put Edit('a=',fr_rep(a))(Skip,2(a));
 =>  Request mk_fr(-3333,0)
     Denominator must not be 0
     IBM0280I  ONCODE=0009  The ERROR condition was raised
               by a SIGNAL statement.
        At offset +00000276 in procedure with entry FT
 *-------------------------------------------------------------------*/
 a=mk_fr(0,3333); Put Edit('a=',fr_rep(a))(Skip,2(a));
 Put Edit('-3,7')(Skip,a);
 a=mk_fr(-3,7);
 b=mk_fr(9,2);
 p=fr_mult(a,b);
 s=fr_add(a,b);
 d=fr_sub(a,b);
 q=fr_div(a,b);
 r=fr_div(b,a);
 Put Edit('a=',fr_rep(a))(Skip,2(a));
 Put Edit('b=',fr_rep(b))(Skip,2(a));
 Put Edit('a*b=',fr_rep(p))(Skip,2(a));
 Put Edit('a+b=',fr_rep(s))(Skip,2(a));
 Put Edit('a-b=',fr_rep(d))(Skip,2(a));
 Put Edit('a/b=',fr_rep(q))(Skip,2(a));
 Put Edit('b/a=',fr_rep(r))(Skip,2(a));
 Put Edit(fr_rep(a),fr_cmp(a,b),fr_rep(b))(Skip,3(a));
 Put Edit(fr_rep(b),fr_cmp(b,a),fr_rep(a))(Skip,3(a));
 Put Edit(fr_rep(a),fr_cmp(a,a),fr_rep(a))(Skip,3(a));

 mk_fr: Proc(n,d) Recursive Returns(Dec Fixed(15) Complex);
 /*--------------------------------------------------------------------
 * make a Complex number
 * normalize and cancel
 *-------------------------------------------------------------------*/
 Dcl (n,d) Dec Fixed(15);
 Dcl (na,da) Dec Fixed(15);
 Dcl res Dec Fixed(15) Complex;
 Dcl x   Dec Fixed(15);
 na=abs(n);
 da=abs(d);
 Select;
   When(n=0) Do;
     real(res)=0;
     imag(res)=1;
     End;
   When(d=0) Do;
     Put Edit('Request mk_fr('!!n_rep(n)!!','!!n_rep(d)!!')')
             (Skip,a);
     Put Edit('Denominator must not be 0')(Skip,a);
     Signal error;
     End;
   Otherwise Do;
     x=gcd(na,da);
     real(res)=sign(n)*sign(d)*na/x;
     imag(res)=da/x;
     End;
   End;
 Return(res);
 End;

 fr_add: Proc(a,b) Returns(Dec Fixed(15) Complex);
 /*--------------------------------------------------------------------
 * add 'fractions' a and b
 *-------------------------------------------------------------------*/
 Dcl (a,b,res)     Dec Fixed(15) Complex;
 Dcl (an,ad,bn,bd) Dec Fixed(15);
 Dcl (rd,rn)       Dec Fixed(15);
 Dcl x             Dec Fixed(15);
 an=real(a);
 ad=imag(a);
 bn=real(b);
 bd=imag(b);
 rd=ad*bd;
 rn=an*bd+bn*ad;
 x=gcd(rd,rn);
 real(res)=rn/x;
 imag(res)=rd/x;
 Return(res);
 End;

 fr_sub: Proc(a,b) Returns(Dec Fixed(15) Complex);
 /*--------------------------------------------------------------------
 * subtract 'fraction' b from a
 *-------------------------------------------------------------------*/
 Dcl (a,b) Dec Fixed(15) Complex;
 Dcl b2    Dec Fixed(15) Complex;
 real(b2)=-real(b);
 imag(b2)=imag(b);
 Return(fr_add(a,b2));
 End;

 fr_mult: Proc(a,b) Returns(Dec Fixed(15) Complex);
 /*--------------------------------------------------------------------
 * multiply 'fractions' a and b
 *-------------------------------------------------------------------*/
 Dcl (a,b,res) Dec Fixed(15) Complex;
 real(res)=real(a)*real(b);
 imag(res)=imag(a)*imag(b);
 Return(res);
 End;

 fr_div: Proc(a,b) Returns(Dec Fixed(15) Complex);
 /*--------------------------------------------------------------------
 * divide 'fraction' a by b
 *-------------------------------------------------------------------*/
 Dcl (a,b) Dec Fixed(15) Complex;
 Dcl b2    Dec Fixed(15) Complex;
 real(b2)=imag(b);
 imag(b2)=real(b);
 If real(a)=0 & real(b)=0 Then
   Return(mk_fr(1,1));
 Return(fr_mult(a,b2));
 End;

 fr_cmp: Proc(a,b) Returns(char(1));
 /*--------------------------------------------------------------------
 * compare 'fractions' a and b
 *-------------------------------------------------------------------*/
 Dcl (a,b)         Dec Fixed(15) Complex;
 Dcl (an,ad,bn,bd) Dec Fixed(15);
 Dcl (a2,b2)       Dec Fixed(15);
 Dcl (rd)          Dec Fixed(15);
 Dcl res           Char(1);
 an=real(a);
 ad=imag(a);
 If ad=0 Then Do;
   Put Edit('ad=',ad,'candidate=',candidate)(Skip,a,f(10));
   Signal Error;
   End;
 bn=real(b);
 bd=imag(b);
 rd=ad*bd;
 a2=abs(an*bd)*sign(an)*sign(ad);
 b2=abs(bn*ad)*sign(bn)*sign(bd);
 Select;
   When(a2<b2) res='<';
   When(a2>b2) res='>';
   Otherwise Do;
     res='=';
     End;
   End;
 Return(res);
 End;

 fr_rep: Proc(f) Returns(char(15) Var);
 /*--------------------------------------------------------------------
 * Return the representation of 'fraction' f
 *-------------------------------------------------------------------*/
 Dcl f     Dec Fixed(15) Complex;
 Dcl res   Char(15) Var;
 Dcl (n,d) Pic'(14)Z9';
 Dcl x     Dec Fixed(15);
 Dcl s     Dec Fixed(15);
 n=abs(real(f));
 d=abs(imag(f));
 x=gcd(n,d);
 s=sign(real(f))*sign(imag(f));
 res=trim(n/x)!!'/'!!trim(d/x);
 If s<0 Then
   res='-'!!res;
 Return(res);
 End;

 n_rep: Proc(x) Returns(char(15) Var);
 /*--------------------------------------------------------------------
 * Return the representation of x
 *-------------------------------------------------------------------*/
 Dcl x     Dec Fixed(15);
 Dcl res   Char(15) Var;
 Put String(res) List(x);
 res=trim(res);
 Return(res);
 End;

 gcd: Proc(a,b) Returns(Dec Fixed(15)) Recursive;
 /*--------------------------------------------------------------------
 * Compute the greatest common divisor
 *-------------------------------------------------------------------*/
 Dcl (a,b) Dec Fixed(15) Nonassignable;
 If b=0 then Return (abs(a));
 Return(gcd(abs(b),mod(abs(a),abs(b))));
 End gcd;

 lcm: Proc(a,b) Returns(Dec Fixed(15));
 /*--------------------------------------------------------------------
 * Compute the least common multiple
 *-------------------------------------------------------------------*/
 Dcl (a,b) Dec Fixed(15) Nonassignable;
 if a=0 ! b=0 then Return (0);
 Return(abs(a*b)/gcd(a,b));
 End lcm;

 End;
Output:
First solve the task at hand
      6 is a perfect number    2    3
     28 is a perfect number    2    4    7   14
    496 is a perfect number    2    4    8   16   31   62  124  248
   8128 is a perfect number    2    4    8   16   32   64  127  254  508 1016 2032 4064

Then try a few things
zero=0/1
a=0/1
a=0/1
-3,7
a=-3/7
b=9/2
a*b=-27/14
a+b=57/14
a-b=-69/14
a/b=-2/21
b/a=1/0
-3/7<9/2
9/2>-3/7
-3/7=-3/7

Prolog

Prolog supports rational numbers, where P/Q is written as P rdiv Q.

divisor(N, Div) :-
    Max is floor(sqrt(N)),
    between(1, Max, D),
    divmod(N, D, _, 0),
    (Div = D; Div is N div D, Div =\= D).

divisors(N, Divs) :-
    setof(M, divisor(N, M), Divs).

recip(A, B) :- B is 1 rdiv A.

sumrecip(N, A) :-
    divisors(N, [1 | Ds]),
    maplist(recip, Ds, As),
    sum_list(As, A).

perfect(X) :- sumrecip(X, 1).

main :-
    Limit is 1 << 19,
    forall(
        (between(1, Limit, N), perfect(N)),
	(format("~w~n", [N]))),
    halt.

?- main.
Output:
6
28
496
8128

Python

Works with: Python version 3.0

Python 3's standard library already implements a Fraction class:

from fractions import Fraction

for candidate in range(2, 2**19):
  sum = Fraction(1, candidate)
  for factor in range(2, int(candidate**0.5)+1):
    if candidate % factor == 0:
      sum += Fraction(1, factor) + Fraction(1, candidate // factor)
  if sum.denominator == 1:
    print("Sum of recipr. factors of %d = %d exactly %s" %
           (candidate, int(sum), "perfect!" if sum == 1 else ""))

It might be implemented like this:

def lcm(a, b):
    return a // gcd(a,b) * b

def gcd(u, v):
    return gcd(v, u%v) if v else abs(u)

class Fraction:
    def __init__(self, numerator, denominator):
        common = gcd(numerator, denominator)
        self.numerator = numerator//common
        self.denominator = denominator//common
    def __add__(self, frac):
        common = lcm(self.denominator, frac.denominator)
        n = common // self.denominator * self.numerator + common // frac.denominator * frac.numerator
        return Fraction(n, common)
    def __sub__(self, frac):
        return self.__add__(-frac)
    def __neg__(self):
        return Fraction(-self.numerator, self.denominator)
    def __abs__(self):
        return Fraction(abs(self.numerator), abs(self.denominator))
    def __mul__(self, frac):
        return Fraction(self.numerator * frac.numerator, self.denominator * frac.denominator)
    def __div__(self, frac):
        return self.__mul__(frac.reciprocal())
    def reciprocal(self):
        return Fraction(self.denominator, self.numerator)
    def __cmp__(self, n):
        return int(float(self) - float(n))
    def __float__(self):
        return float(self.numerator / self.denominator)
    def __int__(self):
        return (self.numerator // self.denominator)

Quackery

Quackery comes with a rational arithmetic library coded in Quackery, bigrat.qky, and documented in The Book of Quackery.pdf. Both are available at the Quackery Github repository.

factors is defined at Factors of an integer#Quackery.

  [ $ "bigrat.qky" loadfile ] now!

  [ -2 n->v rot
    factors witheach 
      [ n->v 1/v v+ ] 
    v0= ]             is perfect ( n -> b )

  19 bit times [ i^ perfect if [ i^ echo cr ] ]
Output:
6
28
496
8128

Racket

Racket always had support for exact rational numbers as a native numeric type.

Example:

-> (* 1/7 14)
2

Raku

(formerly Perl 6)

Works with: rakudo version 2016.08

Raku supports rational arithmetic natively.

(2..2**19).hyper.map: -> $candidate {
    my $sum = 1 / $candidate;
    for 2 .. ceiling(sqrt($candidate)) -> $factor {
        if $candidate %% $factor {
            $sum += 1 / $factor + 1 / ($candidate / $factor);
        }
    }
    if $sum.nude[1] == 1 {
        say "Sum of reciprocal factors of $candidate = $sum exactly", ($sum == 1 ?? ", perfect!" !! ".");
    }
}

Note also that ordinary decimal literals are stored as Rats, so the following loop always stops exactly on 10 despite 0.1 not being exactly representable in floating point:

for 1.0, 1.1, 1.2 ... 10 { .say }

The arithmetic is all done in rationals, which are converted to floating-point just before display so that people don't have to puzzle out what 53/10 means.

REXX

/*REXX program implements a reasonably complete  rational arithmetic  (using fractions).*/
L=length(2**19 - 1)                              /*saves time by checking even numbers. */
     do j=2  by 2  to 2**19 - 1;       s=0       /*ignore unity (which can't be perfect)*/
     mostDivs=eDivs(j);                @=        /*obtain divisors>1; zero sum; null @. */
       do k=1  for  words(mostDivs)              /*unity isn't return from  eDivs  here.*/
       r='1/'word(mostDivs, k);        @=@ r;         s=$fun(r, , s)
       end   /*k*/
     if s\==1  then iterate                      /*Is sum not equal to unity?   Skip it.*/
     say 'perfect number:'       right(j, L)       "   fractions:"            @
     end   /*j*/
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
$div: procedure;  parse arg x;   x=space(x,0);   f= 'fractional division'
      parse var x n '/' d;       d=p(d 1)
      if d=0               then call err  'division by zero:'            x
      if \datatype(n,'N')  then call err  'a non─numeric numerator:'     x
      if \datatype(d,'N')  then call err  'a non─numeric denominator:'   x
      return n/d
/*──────────────────────────────────────────────────────────────────────────────────────*/
$fun: procedure;  parse arg z.1,,z.2 1 zz.2;    arg ,op;  op=p(op '+')
F= 'fractionalFunction';        do j=1  for 2;  z.j=translate(z.j, '/', "_");   end  /*j*/
if abbrev('ADD'      , op)                               then op= "+"
if abbrev('DIVIDE'   , op)                               then op= "/"
if abbrev('INTDIVIDE', op, 4)                            then op= "÷"
if abbrev('MODULUS'  , op, 3) | abbrev('MODULO', op, 3)  then op= "//"
if abbrev('MULTIPLY' , op)                               then op= "*"
if abbrev('POWER'    , op)                               then op= "^"
if abbrev('SUBTRACT' , op)                               then op= "-"
if z.1==''                                               then z.1= (op\=="+" & op\=='-')
if z.2==''                                               then z.2= (op\=="+" & op\=='-')
z_=z.2
                                                 /* [↑]  verification of both fractions.*/
  do j=1  for 2
  if pos('/', z.j)==0    then z.j=z.j"/1";         parse var  z.j  n.j  '/'  d.j
  if \datatype(n.j,'N')  then call err  'a non─numeric numerator:'     n.j
  if \datatype(d.j,'N')  then call err  'a non─numeric denominator:'   d.j
  if d.j=0               then call err  'a denominator of zero:'       d.j
                                               n.j=n.j/1;          d.j=d.j/1
             do  while \datatype(n.j,'W');     n.j=(n.j*10)/1;     d.j=(d.j*10)/1
             end  /*while*/                      /* [↑]   {xxx/1}  normalizes a number. */
  g=gcd(n.j, d.j);    if g=0  then iterate;  n.j=n.j/g;          d.j=d.j/g
  end    /*j*/

 select
 when op=='+' | op=='-' then do;  l=lcm(d.1,d.2);    do j=1  for 2;  n.j=l*n.j/d.j;  d.j=l
                                                     end   /*j*/
                                  if op=='-'  then n.2= -n.2;        t=n.1 + n.2;    u=l
                             end
 when op=='**' | op=='↑'  |,
      op=='^'  then do;  if \datatype(z_,'W')  then call err 'a non─integer power:'  z_
                    t=1;  u=1;     do j=1  for abs(z_);  t=t*n.1;  u=u*d.1
                                   end   /*j*/
                    if z_<0  then parse value   t  u   with   u  t      /*swap  U and T */
                    end
 when op=='/'  then do;      if n.2=0   then call err  'a zero divisor:'   zz.2
                             t=n.1*d.2;    u=n.2*d.1
                    end
 when op=='÷'  then do;      if n.2=0   then call err  'a zero divisor:'   zz.2
                             t=trunc($div(n.1 '/' d.1));    u=1
                    end                           /* [↑]  this is integer division.     */
 when op=='//' then do;      if n.2=0   then call err  'a zero divisor:'   zz.2
                    _=trunc($div(n.1 '/' d.1));     t=_ - trunc(_) * d.1;            u=1
                    end                          /* [↑]  modulus division.              */
 when op=='ABS'  then do;   t=abs(n.1);       u=abs(d.1);        end
 when op=='*'    then do;   t=n.1 * n.2;      u=d.1 * d.2;       end
 when op=='EQ' | op=='='                then return $div(n.1 '/' d.1)  = fDiv(n.2 '/' d.2)
 when op=='NE' | op=='\=' | op=='╪' | ,
                            op=='¬='    then return $div(n.1 '/' d.1) \= fDiv(n.2 '/' d.2)
 when op=='GT' | op=='>'                then return $div(n.1 '/' d.1) >  fDiv(n.2 '/' d.2)
 when op=='LT' | op=='<'                then return $div(n.1 '/' d.1) <  fDiv(n.2 '/' d.2)
 when op=='GE' | op=='≥'  | op=='>='    then return $div(n.1 '/' d.1) >= fDiv(n.2 '/' d.2)
 when op=='LE' | op=='≤'  | op=='<='    then return $div(n.1 '/' d.1) <= fDiv(n.2 '/' d.2)
 otherwise       call err  'an illegal function:'   op
 end   /*select*/

if t==0  then return 0;            g=gcd(t, u);             t=t/g;                   u=u/g
if u==1  then return t
              return t'/'u
/*──────────────────────────────────────────────────────────────────────────────────────*/
eDivs: procedure; parse arg x 1 b,a
         do j=2  while j*j<x;       if x//j\==0  then iterate;   a=a j;   b=x%j b;     end
       if j*j==x  then return a j b;                                            return a b
/*───────────────────────────────────────────────────────────────────────────────────────────────────*/
err:   say;   say '***error*** '    f     " detected"   arg(1);    say;         exit 13
gcd:   procedure; parse arg x,y; if x=0  then return y;  do until _==0; _=x//y; x=y; y=_; end; return x
lcm:   procedure; parse arg x,y; if y=0  then return 0; x=x*y/gcd(x, y);        return x
p:     return word( arg(1), 1)

Programming note:   the   eDivs, gcd, lcm   functions are optimized functions for this program only.

output

perfect number:      6    fractions:  1/2 1/3 1/6
perfect number:     28    fractions:  1/2 1/4 1/7 1/14 1/28
perfect number:    496    fractions:  1/2 1/4 1/8 1/16 1/31 1/62 1/124 1/248 1/496
perfect number:   8128    fractions:  1/2 1/4 1/8 1/16 1/32 1/64 1/127 1/254 1/508 1/1016 1/2032 1/4064 1/8128

Ruby

Ruby has a Rational class in it's core since 1.9.

for candidate in 2 .. 2**19
  sum = Rational(1, candidate)
  for factor in 2 .. Integer.sqrt(candidate)
    if candidate % factor == 0
      sum += Rational(1, factor) + Rational(1, candidate / factor)
    end
  end
  if sum.denominator == 1
    puts "Sum of recipr. factors of %d = %d exactly %s" %
           [candidate, sum.to_i, sum == 1 ? "perfect!" : ""]
  end
end
Output:
Sum of recipr. factors of 6 = 1 exactly perfect!
Sum of recipr. factors of 28 = 1 exactly perfect!
Sum of recipr. factors of 120 = 2 exactly 
Sum of recipr. factors of 496 = 1 exactly perfect!
Sum of recipr. factors of 672 = 2 exactly 
Sum of recipr. factors of 8128 = 1 exactly perfect!
Sum of recipr. factors of 30240 = 3 exactly 
Sum of recipr. factors of 32760 = 3 exactly 
Sum of recipr. factors of 523776 = 2 exactly 

Rust

use std::cmp::Ordering;
use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg};

fn gcd(a: i64, b: i64) -> i64 {
    match b {
        0 => a,
        _ => gcd(b, a % b),
    }
}

fn lcm(a: i64, b: i64) -> i64 {
    a / gcd(a, b) * b
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord)]
pub struct Rational {
    numerator: i64,
    denominator: i64,
}

impl Rational {
    fn new(numerator: i64, denominator: i64) -> Self {
        let divisor = gcd(numerator, denominator);
        Rational {
            numerator: numerator / divisor,
            denominator: denominator / divisor,
        }
    }
}

impl Add for Rational {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        let multiplier = lcm(self.denominator, other.denominator);
        Rational::new(self.numerator * multiplier / self.denominator +
                      other.numerator * multiplier / other.denominator,
                      multiplier)
    }
}

impl AddAssign for Rational {
    fn add_assign(&mut self, other: Self) {
        *self = *self + other;
    }
}

impl Sub for Rational {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        self + -other
    }
}

impl SubAssign for Rational {
    fn sub_assign(&mut self, other: Self) {
        *self = *self - other;
    }
}

impl Mul for Rational {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        Rational::new(self.numerator * other.numerator,
                      self.denominator * other.denominator)
    }
}

impl MulAssign for Rational {
    fn mul_assign(&mut self, other: Self) {
        *self = *self * other;
    }
}

impl Div for Rational {
    type Output = Self;

    fn div(self, other: Self) -> Self {
        self *
        Rational {
            numerator: other.denominator,
            denominator: other.numerator,
        }
    }
}

impl DivAssign for Rational {
    fn div_assign(&mut self, other: Self) {
        *self = *self / other;
    }
}

impl Neg for Rational {
    type Output = Self;

    fn neg(self) -> Self {
        Rational {
            numerator: -self.numerator,
            denominator: self.denominator,
        }
    }
}

impl PartialOrd for Rational {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        (self.numerator * other.denominator).partial_cmp(&(self.denominator * other.numerator))
    }
}

impl<T: Into<i64>> From<T> for Rational {
    fn from(value: T) -> Self {
        Rational::new(value.into(), 1)
    }
}

fn main() {
    let max = 1 << 19;
    for candidate in 2..max {
        let mut sum = Rational::new(1, candidate);
        for factor in 2..(candidate as f64).sqrt().ceil() as i64 {
            if candidate % factor == 0 {
                sum += Rational::new(1, factor);
                sum += Rational::new(1, candidate / factor);
            }
        }

        if sum == 1.into() {
            println!("{} is perfect", candidate);
        }
    }
}

Scala

class Rational(n: Long, d:Long) extends Ordered[Rational]
{
   require(d!=0)
   private val g:Long = gcd(n, d)
   val numerator:Long = n/g
   val denominator:Long = d/g

   def this(n:Long)=this(n,1)

   def +(that:Rational):Rational=new Rational(
      numerator*that.denominator + that.numerator*denominator,
      denominator*that.denominator)

   def -(that:Rational):Rational=new Rational(
      numerator*that.denominator - that.numerator*denominator,
      denominator*that.denominator)

   def *(that:Rational):Rational=
      new Rational(numerator*that.numerator, denominator*that.denominator)

   def /(that:Rational):Rational=
      new Rational(numerator*that.denominator, that.numerator*denominator)

   def unary_~ :Rational=new Rational(denominator, numerator)

   def unary_- :Rational=new Rational(-numerator, denominator)

   def abs :Rational=new Rational(Math.abs(numerator), Math.abs(denominator))

   override def compare(that:Rational):Int=
      (this.numerator*that.denominator-that.numerator*this.denominator).toInt

   override def toString()=numerator+"/"+denominator

   private def gcd(x:Long, y:Long):Long=
      if(y==0) x else gcd(y, x%y)
}

object Rational
{
   def apply(n: Long, d:Long)=new Rational(n,d)
   def apply(n:Long)=new Rational(n)
   implicit def longToRational(i:Long)=new Rational(i)
}
def find_perfects():Unit=
{
   for (candidate <- 2 until 1<<19)
   {
      var sum= ~Rational(candidate)
      for (factor <- 2 until (Math.sqrt(candidate)+1).toInt)
      {
         if (candidate%factor==0)
            sum+= ~Rational(factor)+ ~Rational(candidate/factor)
      }

      if (sum.denominator==1 && sum.numerator==1)
         printf("Perfect number %d sum is %s\n", candidate, sum)
   }
}

Scheme

Scheme has native rational numbers.

Works with: Scheme version R5RS
; simply prints all the perfect numbers
(do ((candidate 2 (+ candidate 1))) ((>= candidate (expt 2 19)))
  (let ((sum (/ 1 candidate)))
    (do ((factor 2 (+ factor 1))) ((>= factor (sqrt candidate)))
      (if (= 0 (modulo candidate factor))
          (set! sum (+ sum (/ 1 factor) (/ factor candidate)))))
    (if (= 1 (denominator sum))
        (begin (display candidate) (newline)))))

It might be implemented like this:

[insert implementation here]

Seed7

The library rational.s7i defines the type rational, which supports the required functionality. Rational numbers are based on the type integer. For rational numbers, which are based on integers with unlimited precision, use bigRational, which is defined in the library bigrat.s7i.

$ include "seed7_05.s7i";
  include "rational.s7i";

const func boolean: isPerfect (in integer: candidate) is func
  result
    var boolean: isPerfect is FALSE;
  local
    var integer: divisor is 0;
    var rational: sum is rational.value;
  begin
    sum := 1 / candidate;
    for divisor range 2 to sqrt(candidate) do
      if candidate mod divisor = 0 then
        sum +:= 1 / divisor + 1 / (candidate div divisor);
      end if;
    end for;
    isPerfect := sum = rat(1);
  end func;

const proc: main is func
  local
    var integer: candidate is 0;
  begin
    for candidate range 2 to 2 ** 19 - 1 do
      if isPerfect(candidate) then
        writeln(candidate <& " is perfect");
      end if;
    end for;
  end func;
Output:
6 is perfect
28 is perfect
496 is perfect
8128 is perfect

Sidef

Sidef has built-in support for rational numbers.

for n in (1 .. 2**19) {
    var frac = 0

    n.divisors.each {|d|
        frac += 1/d
    }

    if (frac.is_int) {
        say "Sum of reciprocal divisors of #{n} = #{frac} exactly #{
            frac == 2 ? '- perfect!' : ''
        }"
    }
}
Output:
Sum of reciprocal divisors of 1 = 1 exactly 
Sum of reciprocal divisors of 6 = 2 exactly - perfect!
Sum of reciprocal divisors of 28 = 2 exactly - perfect!
Sum of reciprocal divisors of 120 = 3 exactly 
Sum of reciprocal divisors of 496 = 2 exactly - perfect!
Sum of reciprocal divisors of 672 = 3 exactly 
Sum of reciprocal divisors of 8128 = 2 exactly - perfect!
Sum of reciprocal divisors of 30240 = 4 exactly 
Sum of reciprocal divisors of 32760 = 4 exactly 
Sum of reciprocal divisors of 523776 = 3 exactly 

Slate

Slate uses infinite-precision fractions transparently.

54 / 7.
20 reciprocal.
(5 / 6) reciprocal.
(5 / 6) as: Float.

Smalltalk

Smalltalk uses naturally and transparently infinite precision fractions (through the class Fraction):

st> 54/7
54/7
st> 54/7 + 1
61/7
st> 54/7 < 50
true
st> 20 reciprocal
1/20
st> (5/6) reciprocal
6/5
st> (5/6) asFloat
0.8333333333333334
Works with: GNU Smalltalk
(and all others)
| sum |
2 to: (2 raisedTo: 19) do: [ :candidate |
  sum := candidate reciprocal.
  2 to: (candidate sqrt) do: [ :factor |
     ( (candidate \\ factor) = 0 )
        ifTrue: [
           sum := sum + (factor reciprocal) + ((candidate / factor) reciprocal)
        ]
  ].
  ( (sum denominator) = 1 )
      ifTrue: [
           ('Sum of recipr. factors of %1 = %2 exactly %3' %
                     { candidate printString . 
                       (sum asInteger) printString . 
                       ( sum = 1 ) ifTrue: [ 'perfect!' ]
                                   ifFalse: [ ' ' ] }) displayNl
      ]
].

Swift

import Foundation

extension BinaryInteger {
  @inlinable
  public func gcd(with other: Self) -> Self {
    var gcd = self
    var b = other

    while b != 0 {
      (gcd, b) = (b, gcd % b)
    }

    return gcd
  }

  @inlinable
  public func lcm(with other: Self) -> Self {
    let g = gcd(with: other)

    return self / g * other
  }
}

public struct Frac<NumType: BinaryInteger & SignedNumeric>: Equatable {
  @usableFromInline
  var _num: NumType

  @usableFromInline
  var _dom: NumType

  @usableFromInline
  init(_num: NumType, _dom: NumType) {
    self._num = _num
    self._dom = _dom
  }

  @inlinable
  public init(numerator: NumType, denominator: NumType) {
    let divisor = numerator.gcd(with: denominator)

    self._num = numerator / divisor
    self._dom = denominator / divisor
  }

  @inlinable
  public static func + (lhs: Frac, rhs: Frac) -> Frac {
    let multiplier = lhs._dom.lcm(with: rhs.denominator)

    return Frac(
      numerator: lhs._num * multiplier / lhs._dom + rhs._num * multiplier / rhs._dom,
      denominator: multiplier
    )
  }

  @inlinable
  public static func += (lhs: inout Frac, rhs: Frac) {
    lhs = lhs + rhs
  }

  @inlinable
  public static func - (lhs: Frac, rhs: Frac) -> Frac {
    return lhs + -rhs
  }

  @inlinable
  public static func -= (lhs: inout Frac, rhs: Frac) {
    lhs = lhs + -rhs
  }

  @inlinable
  public static func * (lhs: Frac, rhs: Frac) -> Frac {
    return Frac(numerator: lhs._num * rhs._num, denominator: lhs._dom * rhs._dom)
  }

  @inlinable
  public static func *= (lhs: inout Frac, rhs: Frac) {
    lhs = lhs * rhs
  }

  @inlinable
  public static func / (lhs: Frac, rhs: Frac) -> Frac {
    return lhs * Frac(_num: rhs._dom, _dom: rhs._num)
  }

  @inlinable
  public static func /= (lhs: inout Frac, rhs: Frac) {
    lhs = lhs / rhs
  }

  @inlinable
  prefix static func - (rhs: Frac) -> Frac {
    return Frac(_num: -rhs._num, _dom: rhs._dom)
  }
}

extension Frac {
  @inlinable
  public var numerator: NumType {
    get { _num }
    set {
      let divisor = newValue.gcd(with: denominator)

      _num = newValue / divisor
      _dom = denominator / divisor
    }
  }

  @inlinable
  public var denominator: NumType {
    get { _dom }
    set {
      let divisor = newValue.gcd(with: numerator)

      _num = numerator / divisor
      _dom = newValue / divisor
    }
  }
}

extension Frac: CustomStringConvertible {
  public var description: String {
    let neg = numerator < 0 || denominator < 0

    return "Frac(\(neg ? "-" : "")\(abs(numerator)) / \(abs(denominator)))"
  }
}

extension Frac: Comparable {
  @inlinable
  public static func <(lhs: Frac, rhs: Frac) -> Bool {
    return lhs._num * rhs._dom < lhs._dom * rhs._num
  }
}

extension Frac: ExpressibleByIntegerLiteral {
  public init(integerLiteral value: Int) {
    self._num = NumType(value)
    self._dom = 1
  }
}


for candidate in 2..<1<<19 {
  var sum = Frac(numerator: 1, denominator: candidate)

  let m = Int(ceil(Double(candidate).squareRoot()))

  for factor in 2..<m where candidate % factor == 0 {
    sum += Frac(numerator: 1, denominator: factor)
    sum += Frac(numerator: 1, denominator: candidate / factor)
  }

  if sum == 1 {
    print("\(candidate) is perfect")
  }
}
Output:
6 is perfect
28 is perfect
496 is perfect
8128 is perfect

Tcl

[This section is included from a subpage and should be edited there, not here.]

Code to find factors of a number not shown:

namespace eval rat {}

proc rat::new {args} {
    if {[llength $args] == 0} {
        set args {0}
    }
    lassign [split {*}$args] n d
    if {$d == 0} {
        error "divide by zero"
    }
    if {$d < 0} {
        set n [expr {-1 * $n}]
        set d [expr {abs($d)}]
    }
    return [normalize $n $d]
}

proc rat::split {args} {
    if {[llength $args] == 1} {
        lassign [::split $args /] n d
        if {$d eq ""} {
            set d 1
        }
    } else {
        lassign $args n d
    }
    return [list $n $d]
}

proc rat::join {rat} {
    lassign $rat n d
    if {$n == 0} {
        return 0
    } elseif {$d == 1} {
        return $n
    } else {
        return $n/$d
    }
}

proc rat::normalize {n d} {
    set gcd [gcd $n $d]
    return [join [list [expr {$n/$gcd}] [expr {$d/$gcd}]]]
}

proc rat::gcd {a b} {
    while {$b != 0} {
        lassign [list $b [expr {$a % $b}]] a b
    }
    return $a
}

proc rat::abs {rat} {
    lassign [split $rat] n d
    return [join [list [expr {abs($n)}] $d]]
}

proc rat::inv {rat} {
    lassign [split $rat] n d
    return [normalize $d $n]
}

proc rat::+ {args} {
    set n 0
    set d 1
    foreach arg $args {
        lassign [split $arg] an ad
        set n [expr {$n*$ad + $an*$d}]
        set d [expr {$d * $ad}]
    }
    return [normalize $n $d]
}

proc rat::- {args} {
    lassign [split [lindex $args 0]] n d
    if {[llength $args] == 1} {
        return [join [list [expr {-1 * $n}] $d]]
    }
    foreach arg [lrange $args 1 end] {
        lassign [split $arg] an ad
        set n [expr {$n*$ad - $an*$d}]
        set d [expr {$d * $ad}]
    }
    return [normalize $n $d]
}

proc rat::* {args} {
    set n 1
    set d 1
    foreach arg $args {
        lassign [split $arg] an ad
        set n [expr {$n * $an}]
        set d [expr {$d * $ad}]
    }
    return [normalize $n $d]
}

proc rat::/ {a b} {
    set r [* $a [inv $b]]
    if {[string match */0 $r]} {
        error "divide by zero"
    }
    return $r
}

proc rat::== {a b} {
    return [expr {[- $a $b] == 0}]
}

proc rat::!= {a b} {
    return [expr { ! [== $a $b]}]
}

proc rat::< {a b} {
    lassign [split [- $a $b]] n d
    return [expr {$n < 0}]
}

proc rat::> {a b} {
    lassign [split [- $a $b]] n d
    return [expr {$n > 0}]
}

proc rat::<= {a b} {
    return [expr { ! [> $a $b]}]
}

proc rat::>= {a b} {
    return [expr { ! [< $a $b]}]
}

################################################
proc is_perfect {num} {
    set sum [rat::new 0]
    foreach factor [all_factors $num] {
        set sum [rat::+ $sum [rat::new 1/$factor]]
    }
    # note, all_factors includes 1, so sum should be 2
    return [rat::== $sum 2]
}

proc get_perfect_numbers {} {
    set t [clock seconds]
    set limit [expr 2**19]
    for {set num 2} {$num < $limit} {incr num} {
        if {[is_perfect $num]} {
            puts "perfect: $num"
        }
    }
    puts "elapsed: [expr {[clock seconds] - $t}] seconds"

    set num [expr {2**12 * (2**13 - 1)}] ;# 5th perfect number
    if {[is_perfect $num]} {
        puts "perfect: $num"
    }
}

source primes.tcl
get_perfect_numbers
Output:
perfect: 6
perfect: 28
perfect: 496
perfect: 8128
elapsed: 477 seconds
perfect: 33550336

TI-89 BASIC

This example is incomplete. Please ensure that it meets all task requirements and remove this message.

While TI-89 BASIC has built-in rational and symbolic arithmetic, it does not have user-defined data types.

Wren

Library: Wren-math
Library: Wren-rat

The latter module already contains support for rational number arithmetic.

import "./math" for Int
import "./rat" for Rat

System.print("The following numbers (less than 2^19) are perfect:")
for (i in 2...(1<<19)) {
    var sum = Rat.new(1, i)
    for (j in Int.properDivisors(i)[1..-1]) sum = sum + Rat.new(1, j)
    if (sum == Rat.one) System.print("  %(i)")
}
Output:
The following numbers (less than 2^19) are perfect:
  6
  28
  496
  8128

zkl

Enough of a Rational class for this task (ie implement the testing code "nicely").

class Rational{  // Weenie Rational class, can handle BigInts
   fcn init(_a,_b){ var a=_a, b=_b; normalize(); }
   fcn toString{
      if(b==1) a.toString()
      else     "%d//%d".fmt(a,b) 
   }
   var [proxy] isZero=fcn{ a==0 };
   fcn normalize{  // divide a and b by gcd
      g:= a.gcd(b);
      a/=g; b/=g;
      if(b<0){ a=-a; b=-b; } // denominator > 0
      self
   }
   fcn abs       { a=a.abs(); self }
   fcn __opNegate{ a=-a;      self }			    // -Rat
   fcn __opAdd(n){
      if(Rational.isChildOf(n)) self(a*n.b + b*n.a, b*n.b); // Rat + Rat
      else self(b*n + a, b);				    // Rat + Int
   }
   fcn __opSub(n){ self(a*n.b - b*n.a, b*n.b) }		    // Rat - Rat
   fcn __opMul(n){
      if(Rational.isChildOf(n)) self(a*n.a, b*n.b);	    // Rat * Rat
      else self(a*n, b);				    // Rat * Int
   }
   fcn __opDiv(n){ self(a*n.b,b*n.a) }			    // Rat / Rat
   fcn __opEQ(r){				       // Rat==Rat, Rat==n
      if(Rational.isChildOf(r)) a==r.a and b=r.b;
      else			b==1   and a==r;
   }
}
foreach p in ([2 .. (2).pow(19)]){
   sum,limit := Rational(1,p), p.toFloat().sqrt();
   foreach factor in ([2 .. limit]){
      if(p%factor == 0) sum+=Rational(1,factor) + Rational(factor,p);
   }
   if(sum.b==1) println("Sum of recipr. factors of %6s = %s exactly%s"
			.fmt(p, sum, (sum==1) and ", perfect." or "."));
}
Output:
Sum of recipr. factors of      6 = 1 exactly, perfect.
Sum of recipr. factors of     28 = 1 exactly, perfect.
Sum of recipr. factors of    120 = 2 exactly.
Sum of recipr. factors of    496 = 1 exactly, perfect.
Sum of recipr. factors of    672 = 2 exactly.
Sum of recipr. factors of   8128 = 1 exactly, perfect.
Sum of recipr. factors of  30240 = 3 exactly.
Sum of recipr. factors of  32760 = 3 exactly.
Sum of recipr. factors of 523776 = 2 exactly.