Convert decimal number to rational

From Rosetta Code
Task
Convert decimal number to rational
You are encouraged to solve this task according to the task description, using any language you may know.

The task is to write a program to transform a decimal number into a fraction in lowest terms.

It is not always possible to do this exactly. For instance, while rational numbers can be converted to decimal representation, some of them need an infinite number of digits to be represented exactly in decimal form. Namely, repeating decimals such as 1/3 = 0.333...

Because of this, the following fractions cannot be obtained (reliably) unless the language has some way of representing repeating decimals:

  • 67 / 74 = 0.9(054) = 0.9054054...
  • 14 / 27 = 0.(518) = 0.518518...
  • 3 / 4 = 0.75

Acceptable output:

  • 0.9054054 → 4527027 / 5000000
  • 0.518518 → 259259 / 500000
  • 0.75 → 3 / 4

Ada

Specification of a procedure Real_To_Rational, which is searching for the best approximation of a real number. The procedure is generic. I.e., you can instantiate it by your favorite "Real" type (Float, Long_Float, ...).

<lang Ada>generic

  type Real is digits <>;

procedure Real_To_Rational(R: Real;

                          Bound: Positive;
                          Nominator: out Integer;
                          Denominator: out Positive);</lang>

The implementation (just brute-force search for the best approximation with Denominator less or equal Bound):

<lang Ada>procedure Real_To_Rational (R: Real;

                           Bound: Positive;
                           Nominator: out Integer;
                           Denominator: out  Positive) is
  Error: Real;
  Best: Positive := 1;
  Best_Error: Real := Real'Last;

begin

  if R = 0.0 then
     Nominator := 0;
     Denominator := 1;
     return;
  elsif R < 0.0 then
     Real_To_Rational(-R, Bound, Nominator, Denominator);
     Nominator := - Nominator;
     return;
  else
     for I in 1 .. Bound loop
        Error := abs(Real(I) * R - Real'Rounding(Real(I) * R));
        if Error < Best_Error then
           Best := I;
           Best_Error := Error;
        end if;
     end loop;
  end if;
  Denominator := Best;
  Nominator   := Integer(Real'Rounding(Real(Denominator) * R));

end Real_To_Rational;</lang>

The main program, called "Convert_Decimal_To_Rational", reads reals from the standard input until 0.0. It outputs progressively better rational approximations of the reals, where "progressively better" means a larger Bound for the Denominator:

<lang Ada>with Ada.Text_IO; With Real_To_Rational;

procedure Convert_Decimal_To_Rational is

  type My_Real is new Long_Float; -- change this for another "Real" type
  package FIO is new Ada.Text_IO.Float_IO(My_Real);
  procedure R2R is new Real_To_Rational(My_Real);
  Nom, Denom: Integer;
  R: My_Real;

begin

  loop
     Ada.Text_IO.New_Line;
     FIO.Get(R);
     FIO.Put(R, Fore => 2, Aft => 9, Exp => 0);
     exit when R = 0.0;
     for I in 0 .. 4 loop
        R2R(R, 10**I, Nom, Denom);
        Ada.Text_IO.Put("  " & Integer'Image(Nom) &
                        " /" & Integer'Image(Denom));
     end loop;
  end loop;

end Convert_Decimal_To_Rational; </lang>

Finally, the output (reading the input from a file):

> ./convert_decimal_to_rational < input.txt

 0.750000000   1 / 1   3 / 4   3 / 4   3 / 4   3 / 4
 0.518518000   1 / 1   1 / 2   14 / 27   14 / 27   14 / 27
 0.905405400   1 / 1   9 / 10   67 / 74   67 / 74   67 / 74
 0.142857143   0 / 1   1 / 7   1 / 7   1 / 7   1 / 7
 3.141592654   3 / 1   22 / 7   22 / 7   355 / 113   355 / 113
 2.718281828   3 / 1   19 / 7   193 / 71   1457 / 536   25946 / 9545
-0.423310825   0 / 1  -3 / 7  -11 / 26  -69 / 163  -1253 / 2960
31.415926536   31 / 1   157 / 5   377 / 12   3550 / 113   208696 / 6643
 0.000000000

AutoHotKey

<lang AutoHotKey>


   Array := []
   inputbox, string, Enter Number
   stringsplit, string, string, .
   if ( string1 = 0 )
           string1 =
   loop, parse, string, .
           if A_index = 2
                   loop, parse, A_loopfield
                                                   Array[A_index] := A_loopfield,          k := A_index
   if (k = 1)
    {
   numerator := Array[1]
   Denominator := 10
   goto label
   }
   Original1 := K
   To_rn := floor(k/2)
   M_M := k - To_rn
   Original2 := k - To_rn
   loop
   {
   loop, % To_rn
    
   {
   Check1 .= Array[k]
   Check2 .= Array[M_M]
   k--
   m_M--
   }
   if ( check1 = check2 )
   {
           ;~ process beginsTO check;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     loop, % To_rn
       nines .= 9
   loop, % k - TO_rn
       Zeroes .= 0
   loop % k - TO_rn
           Minus .= Array[A_index]
   loop % k
           Plus .= Array[A_index]
   if ( minus = "" )
           minus := 0
   Numerator := Plus - minus
   Denominator := Nines . Zeroes
   ;;;;;;;;;;;;;HCF
   goto, label
   }
   Check1 =
   check2 =
   k := Original1
   m_M := original2 + A_index
   TO_rn--
   if ( to_rn = 0 )
   {
           zeroes =
           loop % original1
                   zeroes .= 0
   Denominator := 1 . zeroes
   numerator := string2
   goto, label
   }
   }
   esc::Exitapp
   label:
   Index := 2
   loop
   {
          
           if (mod(denominator, numerator) = 0 )
                   HCF := numerator
           if ( index = floor(numerator/2) )
           break
   if  ( mod(numerator, index) = 0 ) && ( mod(denominator, index) = 0 )
   {
           HCF = %index%
           index++
   }
   else
           index++
   }
   if ( HCF = "" )
           Ans := numerator "/" Denominator
   else
   Ans := floor(numerator/HCF) "/" floor(Denominator/HCF)
   MsgBox % String . "  ->  " . String1 . " " . Ans
   reload

</lang>

BASIC

<lang qbasic> 'Program to transform a decimal to a fraction 'LRCVS 11.06.11

'program to transform a decimal number to fraction declare sub exacto (a$) declare sub puro (a$, b$()) declare sub mixto (a$, b$()) declare function factor (j , k ) as integer

dim as integer l, r, s, t, k, w1, i, m, x, ll, pp, ps, u, v, j

dim as string a, c, d, a2

dim y () as string dim w2 () as string

cls input "Decimal number = ";a$ a2$ = a$ print if instr(a$,".") = 0 then print "It's not a decimal number " : goto 100 cls l = len(a$)

for r = 1 to l for s = 1 to l if s + r = l + 2 then exit for k = k + 1 next s next r

w1 = k redim y$(w1) redim b$(w1)

k = 0 for r = 1 to l for s = 1 to l c$ = mid$(a$,r,s) if s + r = l + 2 then exit for if len(c$) <= int(l/2) then k = k + 1 : y$(k) = c$ next s next r t = 0

for r = 1 to k i = 0 f = 0 x = 0 m = 0 if i = 0 then i = instr(a$,y$(r)):x = 1 for s = 1 to len(a$) if x = 1 then f = instr(s,a$,y$(r)) if x = 1 and f > m then m = f next s

h = 0 k = 0 for n = i to m step len(y$(r)) if h = 0 and mid$(a$,n,len(y$(r))) = y$(r) then k = k + 1 else h = 1 next n if k > 1 then t = t + 1 :b$(t) = y$(r) next r

for r = 1 to w1 for s = r + 1 to w1 if b$(r) = b$(s) then b$(s) = "" next s next r

print "decimal number = ";a$ print

ll = len(a$) pp = instr(a$,".") d$ = mid$(a$,pp+1,ll) ps = instr(d$,b$(1)) if ps = 0 then

               print "Decimal number exact"
               print
               call exacto (a$)
               end if

if ps = 1 then

               print "Decimal number pure"
               print
               call puro (a$, b$())
               end if

if ps > 1 then

                print "Decimal number mix"
                print
                call mixto (a$, b$())
                end if

100: print print "End" sleep end

':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sub exacto (a as string) dim as integer b, c, d, g, may, j, k, l, r, s, u, v, w, f dim as string z, h, g1 b = len(a$) c = instr(a$,".") d = b - c g = int(val(a$)) h$ = right$(a$, b - c)

may = 0 j = 10^d k = val(h$) for n = 9 to 1 step - 1 if j mod (1*(10^n)) = 0 and k mod (1*(10^n)) = 0 then j = j/(1*(10^n)) : k = k/(1*(10^n)) :exit for next n l = factor (j,k) u = k/l v = j/l w = (g * v) + u print print w;"/";v ;" = " ;w/v

end sub

':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sub puro (a as string, b() as string) dim as integer b2, c, d, g, may, j, k, l, u, v, w, f, lr,x5 dim as string z, h, g1, x, a3

z$ = b$(1) x5 = int(val(a$)) lr = len (z$) b2 = len (a$) c = instr (a$,".") g = int (val(a$)) b2 = len(z$) + 1 + len(str$(g)) a3$ = str$(g) + "." + z$ h$ = right$(a3$, b2 - c)

may = 0 x$ = "" for n = 1 to lr x$ = x$ + "9" next n

j = val(x$) k = val(h$)

for n = 9 to 1 step - 1 if j mod (1*(10^n)) = 0 and k mod (1*(10^n)) = 0 then j = j/(1*(10^n)) : k = k/(1*(10^n)) :exit for next n l = factor (j,k) u = k/l v = j/l w = (g * v) + u print w;"/";v ;" = ";w/v print print "Option >>> " call exacto (a$)

end sub

':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sub mixto (a as string, b() as string)

dim as integer b3, c, d, g, j, k, l, u, v, w, f, lr, lz, ly, x5 dim as string z, h, g4, g7, x, y

z$ = b$(1) x5 = int(val(a$)) w = instr(a$, z$) v = instr(a$,".") y$ = mid$(a$,v+1,w-v-1) lz = len(z$) ly = len(y$) b3 = (val(y$)*(9*(10^ly))) + ((1*(10^ly))* (val(z$))) c = (9*(10^ly))*(1*(10^ly)) j = b3 k = c for n = 9 to 1 step - 1 if j mod (1*(10^n)) = 0 and k mod (1*(10^n)) = 0 then j = j/(1*(10^n)) : k = k/(1*(10^n)): exit for next n l = factor (b3,c) u = k/l v = j/l if x5 <> 0 then print (x5*v)+ u;"/";u ;" = ";((x5*v)+ u)/u else print v;"/";u;" = "; v/u print print "Option >>> " call exacto (a$) end sub ':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: function factor (j as integer, k as integer) as integer dim as integer may, n, s, r, l5, j5, k5 may = 0 l5 = 1 j5 = j k5 = k for n = 9 to 1 step - 1 if j5 mod (1*(10^n)) = 0 and k5 mod (1*(10^n)) = 0 then j5 = j5/(1*(10^n)) : k5 = k5/(1*(10^n)): exit for next n if j5 > k5 then may = j5 else may = k5 for n = may to 1 step - 1

  r = (j5 mod n)
  s = (k5 mod n)
  if r = 0 and s = 0  then l5 = n :exit for

next n factor = l5 end function end</lang>

Bracmat

<lang bracmat>( ( exact

 =   integerPart decimalPart z
   .     @(!arg:?integerPart "." ?decimalPart)
       &   !integerPart
         + (   @( !decimalPart
                : (? ((%@:~0) ?:?decimalPart)) [?z
                )
             & !decimalPart*10^(-1*!z)
           | 0
           )
     | !arg
 )

& ( approximation

 =     integerPart firstDecimals repeatingDecimals
     , x y z z-y x-y numerator denominator
   .   @( !arg
        :   ?integerPart
            "."
            [?x
            ?firstDecimals
            ?repeatingDecimals
            [?y
            !repeatingDecimals
            [?z
        )
     & !z+-1*!y:?z-y
     & !x+-1*!y:?x-y
     & 10:?numerator:?denominator
     & ( !z-y:0&0:?repeatingDecimals
       |   9:?denominator
         &   whl
           ' ( !z+-1:>!y:?z
             & !numerator*10:?numerator
             & !denominator*10+9:?denominator
             )
         & @(!repeatingDecimals:? #?repeatingDecimals)
       )
     & ( @(!firstDecimals:? #?firstDecimals)
       | 0:?firstDecimals
       )
     &   !integerPart
       + !firstDecimals*10^(!x-y+!z-y)
       + !numerator*!denominator^-1*!repeatingDecimals*10^!x-y
 )

& "0.9054054054"

     "0.5185185185"
     "0.75"
     "0.905405400"
     "0.1428571428"
     "35.000"
     "35.001"
     "0.00000000001"
     "0.000001000001"
     "0.9"
     "0.99"
     "0.909"
     "0.9090"
     "0.90909"
 : ?decs

& whl

 ' ( !decs:%?dec ?decs
   & approximation$!dec:?approx
   &   out
     $ ( !dec
         "="
         (exact$!dec:?precise)
         ( !approx:!precise&
         | str$("(approx. " !approx ")")
         )
       )
   )

);</lang> Output:

0.9054054054 = 4527027027/5000000000 (approx. 67/74)
0.5185185185 = 1037037037/2000000000 (approx. 14/27)
0.75 = 3/4
0.905405400 = 4527027/5000000
0.1428571428 = 357142857/2500000000
35.000 = 35
35.001 = 35001/1000
0.00000000001 = 1/100000000000
0.000001000001 = 1000001/1000000000000 (approx. 1/999999)
0.9 = 9/10
0.99 = 99/100 (approx. 1)
0.909 = 909/1000
0.9090 = 909/1000 (approx. 10/11)
0.90909 = 90909/100000 (approx. 10/11)

C

Since the intention of the task giver is entirely unclear, here's another version of best rational approximation of a floating point number. It's somewhat more rigorous than the Perl version below, but is still not quite complete.<lang C>#include <stdio.h>

  1. include <stdlib.h>
  2. include <math.h>
  3. include <stdint.h>

/* f : number to convert.

* num, denom: returned parts of the rational.
* md: max denominator value.  Note that machine floating point number
*     has a finite resolution (10e-16 ish for 64 bit double), so specifying
*     a "best match with minimal error" is often wrong, because one can
*     always just retrieve the significand and return that divided by 
*     2**52, which is in a sense accurate, but generally not very useful:
*     1.0/7.0 would be "2573485501354569/18014398509481984", for example.
*/

void rat_approx(double f, int64_t md, int64_t *num, int64_t *denom) { /* a: continued fraction coefficients. */ int64_t a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 }; int64_t x, d, n = 1; int i, neg = 0;

if (md <= 1) { *denom = 1; *num = (int64_t) f; return; }

if (f < 0) { neg = 1; f = -f; }

while (f != floor(f)) { n <<= 1; f *= 2; } d = f;

/* continued fraction and check denominator each step */ for (i = 0; i < 64; i++) { a = n ? d / n : 0; if (i && !a) break;

x = d; d = n; n = x % n;

x = a; if (k[1] * a + k[0] >= md) { x = (md - k[0]) / k[1]; if (x * 2 >= a || k[1] >= md) i = 65; else break; }

h[2] = x * h[1] + h[0]; h[0] = h[1]; h[1] = h[2]; k[2] = x * k[1] + k[0]; k[0] = k[1]; k[1] = k[2]; } *denom = k[1]; *num = neg ? -h[1] : h[1]; }

int main() { int i; int64_t d, n; double f;

printf("f = %16.14f\n", f = 1.0/7); for (i = 1; i <= 20000000; i *= 16) { printf("denom <= %d: ", i); rat_approx(f, i, &n, &d); printf("%lld/%lld\n", n, d); }

printf("\nf = %16.14f\n", f = atan2(1,1) * 4); for (i = 1; i <= 20000000; i *= 16) { printf("denom <= %d: ", i); rat_approx(f, i, &n, &d); printf("%lld/%lld\n", n, d); }

return 0; }</lang>Output:<lang>f = 0.14285714285714 denom <= 1: 0/1 denom <= 16: 1/7 denom <= 256: 1/7 denom <= 4096: 1/7 denom <= 65536: 1/7 denom <= 1048576: 1/7 denom <= 16777216: 1/7

f = 3.14159265358979 denom <= 1: 3/1 denom <= 16: 22/7 denom <= 256: 355/113 denom <= 4096: 355/113 denom <= 65536: 104348/33215 denom <= 1048576: 3126535/995207 denom <= 16777216: 47627751/15160384</lang>

D

Translation of: Ada

<lang d>import std.stdio, std.math, std.string, std.typecons;

alias Fraction = Tuple!(int,"nominator", uint,"denominator");

Fraction real2Rational(in real r, in uint bound) /*pure*/ nothrow {

   if (r == 0.0) {
       return Fraction(0, 1);
   } else if (r < 0.0) {
       auto result = real2Rational(-r, bound);
       result.nominator = -result.nominator;
       return result;
   } else {
       uint best = 1;
       real bestError = real.max;
       foreach (i; 1 .. bound + 1) {
           // round is not pure.
           immutable real error = abs(i * r - round(i * r));
           if (error < bestError) {
               best = i;
               bestError = error;
           }
       }
       return Fraction(cast(int)round(best * r), best);
   }

}

void main() {

   immutable tests = [ 0.750000000,  0.518518000, 0.905405400,
                       0.142857143,  3.141592654, 2.718281828,
                      -0.423310825, 31.415926536];
   foreach (r; tests) {
       writef("%8.9f  ", r);
       foreach (i; 0 .. 5)
           writef("  %d/%d", real2Rational(r, 10 ^^ i).tupleof);
       writeln();
   }

}</lang>

Output:
0.750000000    1/1  3/4  3/4  3/4  3/4  3/4
0.518518000    1/1  1/2  14/27  14/27  14/27  37031/71417
0.905405400    1/1  9/10  67/74  67/74  67/74  67/74
0.142857143    0/1  1/7  1/7  1/7  1/7  1/7
3.141592654    3/1  22/7  22/7  355/113  355/113  104348/33215
2.718281828    3/1  19/7  193/71  1457/536  25946/9545  222630/81901
-0.423310825    0/1  -3/7  -11/26  -69/163  -1253/2960  -10093/23843
31.415926536    31/1  157/5  377/12  3550/113  208696/6643  2918194/92889

Go

Go has no native decimal representation so strings are used as input here. The program parses it into a Go rational number, which automatically reduces. <lang go>package main

import (

   "fmt"
   "math/big"

)

func main() {

   for _, d := range []string{"0.9054054", "0.518518", "0.75"} {
       if r, ok := new(big.Rat).SetString(d); ok {
           fmt.Println(d, "=", r)
       } else {
           fmt.Println(d, "invalid decimal number")
       }
   }

}</lang> Output:

0.9054054 = 4527027/5000000
0.518518 = 259259/500000
0.75 = 3/4

Haskell

Note that the decimal values of the task description are truncated in some cases.

The first map finds the simplest fractions within a given radius, because the floating-point representation is not exact. The second line shows that the numbers could be parsed into fractions at compile time if they are given the right type. The last converts the string representation of the given values directly to fractions. <lang haskell>Prelude> map (\d -> Ratio.approxRational d 0.0001) [0.9054054, 0.518518, 0.75] [67 % 74,14 % 27,3 % 4] Prelude> [0.9054054, 0.518518, 0.75] :: [Rational] [4527027 % 5000000,259259 % 500000,3 % 4] Prelude> map (fst . head . Numeric.readFloat) ["0.9054054", "0.518518", "0.75"] :: [Rational] [4527027 % 5000000,259259 % 500000,3 % 4]</lang>

J

J's x: built in will find a rational number which "best matches" a floating point number.

<lang j> x: 0.9054054 0.518518 0.75 NB. find "exact" rational representation 127424481939351r140737488355328 866492568306r1671094481399 3r4</lang>

These numbers are ratios where the integer on the left of the r is the numerator and the integer on the right of the r is the denominator.

Note that the concept of "best" has to do with the expected precision of the argument: <lang j> x: 0.9 0.5 9r10 1r2

  x: 0.9054 0.5185

4527r5000 1037r2000

  x: 0.9054054 0.5185185

127424481939351r140737488355328 1037037r2000000

  x: 0.9054054054 0.5185185185

5358191125333r5918002138463 6073341499873r11712872893031

  x: 0.9054054054054 0.5185185185185

67r74 14r27

  x: 0.9054054054054054 0.5185185185185185

67r74 14r27</lang>

Note that J allows us to specify an epsilon, for the purpose of recognizing a best fit, but the allowed values must be rather small. In J version 6, the value 5e_11 was nearly the largest epsilon allowed:

<lang j> x:(!. 5e_11) 0.9054054054 0.5185185185 67r74 14r27</lang>

(Note that epsilon will be scaled by magnitude of the largest number involved in a comparison when testing floating point representations of numbers for "equality". Note also that this J implementation uses 64 bit ieee floating point numbers.)

Here are some other alternatives for dealing with decimals and fractions:

<lang j> 0j10": x:inv x: 0.9054054 0.518518 0.75 NB. invertible (shown to 10 decimal places) 0.9054054000 0.5185180000 0.7500000000

  0j10": x:inv 67r74 42r81 3r4             NB. decimal representation (shown to 10 decimal places)

0.9054054054 0.5185185185 0.7500000000

  x: x:inv 67r74 42r81 3r4                 NB. invertible

67r74 14r27 3r4</lang>

Liberty BASIC

<lang lb> ' Uses convention that one repeating sequence implies infinitely repeating sequence.. ' Non-recurring fractions are limited to nd number of digits in nuerator & denominator

nd =3 ' suggest 3. 4 is slow. >4 is ....... do

   read x$
   data "0.5", "0.1", "0.333", "1 /3", "0.33", "0.14159265", "2^-0.5", "0.1 +0.9*rnd(1)"
   data "0.142857142857", "int( 1000*rnd(1))/int( 1000*rnd(1))","end"   '   always between 0 and 0.999999...
   if x$ ="end" then exit do
   print x$; " is ";
   type$ =check$( x$)
   print type$;
   if type$ ="recurring" then
       x   =val( mid$( x$, 3, ( len( x$) -2) /2))
       rep =( len( x$) -2) /2
       num =x
       den =10^rep -1
       gcd =gcd( num, den)
       print
       print " Calculating exact fraction for ", recurring$( x); " recurring & found";
       print num /gcd; " /"; den /gcd
       print
   else    '   non-recurring. Check numerators & denominators <1000
       x =eval( x$)
       print
       print " Looking for fractions that are close to "; using( "#.############", x); " & found ";
       eps =10^nd
       for n = 1 to nd
           for i =1 to 10^n -1
               for j =i to 10^n -1
                   fr =i /j
                   if abs( x -fr) <eps then
                       eps =abs( x -fr)
                       'print i; " /"; j; " = ", using( "##.############", fr), "with error +/-"; using( "###.#########", eps /x *100); " %"
                       ii =i: jj =j
                       if eps =0 then exit for
                   end if
               next j
               scan
               if eps =0 then exit for
           next i
           if eps =0 then exit for
       next n
       print ii; " /"; jj
       print
   end if

loop until 0

print print "END."

end

function recurring$( x)

   recurring$ ="0."
   do
       recurring$ =recurring$ +str$( x)
   loop until len( recurring$) >=14

end function

function gcd( a, b) ' thanks Uncle Ben..

   while b <>0
      t =b
      b =a mod b
      a =t
   wend
   gcd =a

end function

function check$( i$)

   check$ ="non-recurring"
   length =len( i$) -2     '   allow for the '0.'.
   if length /2 =int( length /2) then if mid$( i$, 3, length /2) =mid$( i$, 3 +length /2, length /2) then check$ ="recurring"

end function </lang>

 0.5 is non-recurring
 Looking for fractions that are close to 0.500000000000 & found 1 /2

0.1 is non-recurring
 Looking for fractions that are close to 0.100000000000 & found 1 /10

0.333 is non-recurring
 Looking for fractions that are close to 0.333000000000 & found 332 /997

1 /3 is non-recurring
 Looking for fractions that are close to 0.333333333333 & found 1 /3

0.33 is recurring
 Calculating exact fraction for           0.333333333333 recurring & found1 /3

0.14159265 is non-recurring
 Looking for fractions that are close to 0.141592650000 & found 16 /113

2^-0.5 is non-recurring
 Looking for fractions that are close to 0.707106781187 & found 408 /577

0.1 +0.9*rnd(1) is non-recurring
 Looking for fractions that are close to 0.489587274383 & found 47 /96

0.142857142857 is recurring
 Calculating exact fraction for           0.142857142857 recurring & found1 /7

int( 1000*rnd(1))/int( 1000*rnd(1)) is non-recurring
 Looking for fractions that are close to 0.032786885246 & found 2 /61
END.

Julia

Julia has a native Rational type, and provides a convenience conversion function that implements a standard algorithm for approximating a floating-point number by a ratio of integers to within a given tolerance, which defaults to machine epsilon.

<lang Julia>rational(0.9054054) rational(0.518518) rational(0.75)</lang>

4527027//5000000
259259//500000
3//4

Since Julia by default uses its Float64 type to represent floating-point numbers, if enough decimal digits are provided (so that the difference between the floating-point representation of the resulting fraction and the original number is smaller than the machine epsilon) the smaller fraction is returned, which in this case is the exact result:

julia> rational(0.5185185185185185)
14//27
julia> rational(0.9054054054054054)
67//74

Maple

<lang Maple> > map( convert, [ 0.9054054, 0.518518, 0.75 ], 'rational', 'exact' );

                         4527027  259259
                        [-------, ------, 3/4]
                         5000000  500000

</lang>

Mathematica

<lang Mathematica>Map[Rationalize[#,0]&,{0.9054054,0.518518, 0.75} ] -> {4527027/5000000,259259/500000,3/4}</lang>

MATLAB / Octave

<lang Matlab>

 [a,b]=rat(.75)
 [a,b]=rat(.518518)
 [a,b]=rat(.9054054)

</lang>

Output:

  >>   [a,b]=rat(.75)
  a =  3
  b =  4
  >>   [a,b]=rat(.518518)
  a =  37031
  b =  71417
  >>   [a,b]=rat(.9054054)
  a =  67
  b =  74

NetRexx

Now the nearly equivalent program. <lang netrexx> /*NetRexx program to convert decimal numbers to fractions *************

  • 16.08.2012 Walter Pachl derived from Rexx Version 2
                                                                                                                                            • /

options replace format comments java crossref savelog symbols

 Numeric Digits 10               /* use "only" 10 digs of precision */
 ratt('0.9054054054','67/74')
 ratt('0.5185185185','14/27')
 ratt('0.75'        ,'3/4')
 ratt('0.905405400',' 693627417/766095958')
 ratt('0.9054054054','67/74')
 ratt('0.1428571428','1/7')
 ratt('35.000','35')
 ratt('35.001','35001/1000')
 ratt('0.00000000001','?')
 ratt('0.000001000001','1/999999')

ratt(0.9054054054,'1/3')


method ratt(d = Rexx,fs = Rexx) public static

fract=rat(d)
Say '  'd '->' fract
Parse fract no '/' de
If de= Then x=no
         Else x=no/de
If x<>d Then
  Say '> '||x 'is different'

method rat(in, high=) public static /**********************************************************************

  • rat(number<,high) returns a fraction or an integer that is equal to
  • or approximately equal to number.
  • Nominator and denominator must not have more than high digits
  • 16.08.2012 Walter Pachl derived from Rexx Version 2
                                                                                                                                            • /
 if high== then
   high=10**(digits - 1)           /* maximum nominator/denominator */ 
 x=in                                 /* working copy               */
 nom=0                                /* start values nominator     */
 den=1                                /*              denominator   */
 tnom=1                               /*         temp nominator     */
 tden=0                               /*         temp denominator   */
 loop While tnom<=high & tden<=high   /* nominator... not too large */
   n=x.trunc()                        /* take integer part of x     */
   z=tnom;                            /* save temp nominator        */
   tnom=n*tnom+nom;                   /* compute new temp nominator */
   nom=z                              /* assign nominator           */
   z=tden;                            /* save temp denominator      */
   tden=n*tden+den                    /* compute new temp denominato*/
   den=z                              /* assign denominator         */
   if n=x | tnom/tden=in then do
     if tnom>high | tden>high then    /* temp value(s) too large    */
       Leave                          /* don't use them             */
     nom=tnom                         /* otherwise take them as     */
     den=tden                         /* final values               */
     leave                            /* and end the loop           */
     end
   x=1/(x-n)                          /* compute x for next round   */
   end
 If den=1 Then Return nom             /* an integer                 */
          Else Return nom'/'den       /* otherwise a fraction       */</lang>

Output is the same as fro Rexx Version 2.

PARI/GP

Quick and dirty. <lang parigp>convert(x)={

 my(n=0);
 while(x-floor(x*10^n)/10^n!=0.,n++);
 floor(x*10^n)/10^n

};</lang>

To convert a number into a rational with a denominator not dividing a power of 10, use contfrac and the Gauss-Kuzmin distribution to distinguish (hopefully!) where to truncate.

Perl

Note: the following is considerably more complicated than what was specified, because the specification is not, well, specific. Three methods are provided with different interpretation of what "conversion" means: keeping the string representation the same, keeping machine representation the same, or find best approximation with denominator in a reasonable range. None of them takes integer overflow seriously (though the best_approx is not as badly subject to it), so not ready for real use. <lang perl>sub gcd {

       my ($m, $n) = @_;
       ($m, $n) = ($n, $m % $n) while $n;
       return $m

}

sub rat_machine {

       my $n = shift;
       my $denom = 1;
       while ($n != int $n) {
               # assuming the machine format is base 2, and multiplying
               # by 2 doesn't change the mantissa
               $n *= 2;
               # multiply denom by 2, ignoring (very) possible overflow
               $denom <<= 1;
       }
       if ($n) {
               my $g = gcd($n, $denom);
               $n /= $g;
               $denom /= $g;
       }
       return $n, $denom;

}

  1. helper, make continued fraction back into normal fraction

sub get_denom {

       my ($num, $denom) = (1, pop @_);
       for (reverse @_) {
               ($num, $denom) = ($denom, $_ * $denom + $num);
       }
       wantarray ? ($num, $denom) : $denom

}

sub best_approx {

       my ($n, $limit) = @_;
       my ($denom, $neg);
       if ($n < 0) {
               $neg = 1;
               $n = -$n;
       }
       my $int = int($n);
       my ($num, $denom, @coef) = (1, $n - $int);
       # continued fraction, sort of
       while (1) {
               # make sure it terminates
               last if $limit * $denom < 1;
               my $i = int($num / $denom);
               # not the right way to get limit, but it works
               push @coef, $i;
               if (get_denom(@coef) > $limit) {
                       pop @coef;
                       last;
               }
               # we lose precision here, but c'est la vie
               ($num, $denom) = ($denom, $num - $i * $denom);
       }
       ($num, $denom) = get_denom @coef;
       $num += $denom * $int;
       return $neg ? -$num : $num, $denom;

}

sub rat_string {

       my $n = shift;
       my $denom = 1;
       my $neg;
       # trival xyz.0000 ... case
       $n =~ s/\.0+$//;
       return $n, 1 unless $n =~ /\./;
       if ($n =~ /^-/) {
               $neg = 1;
               $n =~ s/^-//;
       }
       # shift decimal point to the right till it's gone
       $denom *= 10    while $n =~ s/\.(\d)/$1\./;
       $n =~ s/\.$//;
       # removing leading zeros lest it looks like octal
       $n =~ s/^0*//;
       if ($n) {
               my $g = gcd($n, $denom);
               $n /= $g;
               $denom /= $g;
       }
       return $neg ? -$n : $n, $denom;

}

my $limit = 1e8; my $x = 3/8; print "3/8 = $x:\n"; printf "machine: %d/%d\n", rat_machine $x; printf "string: %d/%d\n", rat_string $x; printf "approx below $limit: %d/%d\n", best_approx $x, $limit;

$x = 137/4291; print "\n137/4291 = $x:\n"; printf "machine: %d/%d\n", rat_machine $x; printf "string: %d/%d\n", rat_string $x; printf "approx below $limit: %d/%d\n", best_approx $x, $limit;

$x = sqrt(1/2); print "\n1/sqrt(2) = $x\n"; printf "machine: %d/%d\n", rat_machine $x; printf "string: %d/%d\n", rat_string $x; printf "approx below 10: %d/%d\n", best_approx $x, 10; printf "approx below 100: %d/%d\n", best_approx $x, 100; printf "approx below 1000: %d/%d\n", best_approx $x, 1000; printf "approx below 10000: %d/%d\n", best_approx $x, 10000; printf "approx below 100000: %d/%d\n", best_approx $x, 100000; printf "approx below $limit: %d/%d\n", best_approx $x, $limit;

$x = -4 * atan2(1,1); print "\n-Pi = $x\n"; printf "machine: %d/%d\n", rat_machine $x; printf "string: %d/%d\n", rat_string $x;

for (map { 10 ** $_ } 1 .. 10) {

       printf "approx below %g: %d / %d\n", $_, best_approx($x, $_)

}</lang>

Output:

3/8 = 0.375:
machine: 3/8
string:  3/8
approx below 100000000:  3/8

137/4291 = 0.0319272896760662:
machine: 2300603678209305/72057594037927936
string:  159636448380331/5000000000000000
approx below 100000000:  137/4291

1/sqrt(2) = 0.707106781186548
machine: 6369051672525773/9007199254740992
string:  176776695296637/250000000000000
approx below 10:  5/7
approx below 100:  29/41
approx below 1000:  408/577
approx below 10000:  5741/8119
approx below 100000:  33461/47321
approx below 100000000:  38613965/54608393

-Pi = -3.14159265358979
machine: -884279719003555/281474976710656
string:  -314159265358979/100000000000000
approx below 10: -22 / 7
approx below 100: -22 / 7
approx below 1000: -355 / 113
approx below 10000: -355 / 113
approx below 100000: -208341 / 66317
approx below 1e+06: -1146408 / 364913
approx below 1e+07: -5419351 / 1725033
approx below 1e+08: -245850922 / 78256779
approx below 1e+09: -1881244168 / 598818617
approx below 1e+10: -9978066541 / 3176117225

Perl 6

Decimals are natively represented as rationals in Perl 6, so if the task does not need to handle repeating decimals, it is trivially handled by the .nude method, which returns the numerator and denominator: <lang perl6>say .nude.join('/') for 0.9054054, 0.518518, 0.75;</lang>

Output:
4527027/5000000
259259/500000
3/4

However, if we want to take repeating decimals into account, then we can get a bit fancier. <lang perl6>sub decimal_to_fraction ( Str $n, Int $rep_digits = 0 ) returns Str {

   my ( $int, $dec ) = ( $n ~~ /^ (\d+) \. (\d+) $/ )».Str or die;
   my ( $numer, $denom ) = ( $dec, 10 ** $dec.bytes );
   if $rep_digits {
       my $to_move = $dec.bytes - $rep_digits;
       $numer -= $dec.substr(0, $to_move);
       $denom -= 10 ** $to_move;
   }
   my $rat = Rat.new( $numer.Int, $denom.Int ).perl;
   return $int ?? "$int $rat" !! $rat;

}

my @a = ['0.9054', 3], ['0.518', 3], ['0.75', 0], (^4).map({['12.34567', $_]}); for @a -> [ $n, $d ] {

   say "$n with $d repeating digits = ", decimal_to_fraction( $n, $d );

}</lang>

Output:
0.9054 with 3 repeating digits = 67/74
0.518 with 3 repeating digits = 14/27
0.75 with 0 repeating digits = 3/4
12.34567 with 0 repeating digits = 12 34567/100000
12.34567 with 1 repeating digits = 12 31111/90000
12.34567 with 2 repeating digits = 12 17111/49500
12.34567 with 3 repeating digits = 12 1279/3700

PL/I

<lang>(size, fofl): Convert_Decimal_To_Rational: procedure options (main); /* 14 January 2014, from Ada */

Real_To_Rational: procedure (R, Bound, Numerator, Denominator) recursive

        options (reorder);
  declare R float (18), Bound float,
         (Numerator, Denominator) fixed binary (31);
  declare Error float;
  declare Best fixed binary initial (1);
  declare Best_Error float initial (huge(error));
  declare I fixed binary (31);
  if R = 0 then
     do;
        Numerator   = 0;
        Denominator = 1;
        return;
     end;
  else if R < 0 then
     do;
        call Real_To_Rational(-R, Bound, Numerator, Denominator);
        Numerator = -Numerator;
        return;
     end;
  else
     do I = 1 to Bound;
        Error = abs(I * R - trunc(I * R + sign(R)*0.5));
        if Error < Best_Error then
           do;
              Best = I;
              Best_Error = Error;
           end;
     end;
  Denominator = Best;
  Numerator   = Denominator * R + sign(R) * 0.5;

end Real_To_Rational;


  declare (Num, Denom) fixed binary (31);
  declare R float (18);
  declare I fixed BINARY;
  do R = 0.75, 0.25,   0.3333333,   0.518518000,  0.905405400,
         0.142857143,  3.141592654, 2.718281828, -0.423310825,
         31.415926536, 0;
     put skip edit(R) (f(13,9));
     do I = 0 to 4;
        call Real_to_Rational(R, 10**I, Num, Denom);
        put edit('   ' || trim(Num) || ' / ' || trim(Denom)) (a);
     end;
  end;

end Convert_Decimal_To_Rational;</lang> Output:

  0.750000000   1 / 1   3 / 4   3 / 4   3 / 4   3 / 4
  0.250000000   0 / 1   1 / 4   1 / 4   1 / 4   1 / 4
  0.333333300   0 / 1   1 / 3   1 / 3   1 / 3   1 / 3
  0.518518000   1 / 1   1 / 2   14 / 27   14 / 27   14 / 27
  0.905405400   1 / 1   9 / 10   67 / 74   67 / 74   67 / 74
  0.142857143   0 / 1   1 / 7   1 / 7   1 / 7   1 / 7
  3.141592654   3 / 1   22 / 7   22 / 7   355 / 113   355 / 113
  2.718281828   3 / 1   19 / 7   193 / 71   1457 / 536   25946 / 9545
 -0.423310825   0 / 1   -3 / 7   -11 / 26   -69 / 163   -1253 / 2960
 31.415926536   31 / 1   157 / 5   377 / 12   3550 / 113   208696 / 6643
  0.000000000   0 / 1   0 / 1   0 / 1   0 / 1   0 / 1

Python

Works with: Python version 2.6+

Note that the decimal values of the task description are truncated in some cases.

The first loop limits the size of the denominator, because the floating-point representation is not exact. The second converts the string representation of the given values directly to fractions. <lang python>>>> from fractions import Fraction >>> for d in (0.9054054, 0.518518, 0.75): print(d, Fraction.from_float(d).limit_denominator(100))

0.9054054 67/74 0.518518 14/27 0.75 3/4 >>> for d in '0.9054054 0.518518 0.75'.split(): print(d, Fraction(d))

0.9054054 4527027/5000000 0.518518 259259/500000 0.75 3/4 >>> </lang>

Racket

Racket has builtin exact and inexact representantions of numbers, 3/4 is a valid number syntactically, and one can change between the exact and inexact values with the functions showed in the example. They have some amount of inaccuracy, but i guess it can be tolerated. <lang Racket>#lang racket

(inexact->exact 0.75)  ; -> 3/4 (exact->inexact 3/4)  ; -> 0.75

(exact->inexact 67/74) ; -> 0.9054054054054054 (inexact->exact 0.9054054054054054) ;-> 8155166892806033/9007199254740992</lang>

REXX

Version 1

This REXX example supports almost any form of input:

  • ±nnn.ddd
  • ±nnn.dddE±ppp
  • nnn/ddd
  • ±nnn.nnn/±ddd.ddd
  • denominator is optional (but if a   /   is used, it must be present)
  • superfluous blanks are permitted (for whitespace)
  • leading zeroes are permitted
  • leading signs are permitted
  • improper fractions are permitted


REXX can support almost any number of digits, but   10   was chosen for practicality. <lang rexx>/*REXX program converts a fraction [n/m] to it's simplest (lowest) terms*/ numeric digits 10 /*use "only" 10 digs of precision*/ parse arg orig 1 n.1 '/' n.2; if n.2= then n.2=1 if n.1= then call er 'no argument specified.'

        do i=1  for 2                 /*validate both args:  n.1  n.2  */
        if \datatype(n.i,'N')  then call er "argument isn't numeric:" n.i
        end   /*i*/

if n.2=0 then call er "divisor can't be zero." /*whoa, dividing by 0*/ say 'old =' space(orig) /*display original. */ say 'new =' rat(n.1/n.2) /*display the result.*/ exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────ER subroutine───────────────────────*/ er: say; say '***error!***'; say; say arg(1); say; exit 13 /*──────────────────────────────────RAT subroutine──────────────────────*/ rat: procedure; parse arg x 1 _x,y; if y== then y=10**(digits()-1) b=0; g=0; a=1; h=1 /*Y is the tolerance.*/

                          do  while  a<=y & g<=y;      n=trunc(_x)
                          _=a;   a=n*a+b;   b=_
                          _=g;   g=n*g+h;   h=_
                          if n=_x | a/g=x then do
                                               if a>y | g>y  then iterate
                                               b=a;     h=g;      leave
                                               end
                          _x=1/(_x-n)
                          end   /*while a≤y & g≤y*/

if h==1 then return b /*don't show number divided by 1 */

             return b'/'h             /*show a proper/improper fraction*/</lang>

output when using various inputs (which are displayed as part of the output)
Multiple runs are shown, outputs are separated by a blank line.

old = 0.9054054054
new = 67/74

old = 0.5185185185
new = 14/27

old = 0.75
new = 3/4

old = 0.905405400
new = 693627417/766095958

old = 0.9054054054
new = 67/74

old = 0.1428571428
new = 1/7

Version 2

<lang rexx>/*REXX program to convert decimal numbers to fractions ****************

  • 15.08.2012 Walter Pachl derived from above for readability
  • It took me time to understand :-) I need descriptive variable names
  • Output shows where the fraction only approximates the number
  • due to the limit (high) imposed on nominator and denominator
                                                                                                                                            • /
 Numeric Digits 10               /* use "only" 10 digs of precision */
 Call test '0.9054054054','67/74'
 Call test '0.5185185185','14/27'
 Call test '0.75'        ,'3/4'
 Call test '0.905405400',' 693627417/766095958'
 Call test '0.9054054054','67/74'
 Call test '0.1428571428','1/7'
 Call test '35.000','35'
 Call test '35.001','35001/1000'
 Call test '0.00000000001','?'
 Call test '0.000001000001','1/999999'
 Exit

test: /**********************************************************************

  • Test driver for rat
                                                                                                                                            • /
 Parse Arg d,fs                     /* number and expected fraction */
 fh=rat(d)                          /* convert number to fracrion   */
 Call o '  'd fh
 If fh<>fs Then Call o '                   not='fs
 interpret 'x='fh                   /* compute value of fraction    */
 If x<>d Then                       /* not exactly equal to number  */
   Call o '> '||x 'is different'
 Call o ' '
 Return

o: Say arg(1); Return

rat: procedure /**********************************************************************

  • rat(number<,high) returns a fraction or an integer that is equal to
  • or approximately equal to number.
  • Nominator and denominator must not have more than high digits
  • 15.08.2012 Walter Pachl derived from Version 1
                                                                                                                                            • /

parse arg in,high

 x=in                                 /* working copy               */
 if high== then
   high=10**(digits()-1)           /* maximum nominator/denominator */
 nom=0                                /* start values nominator     */
 den=1                                /*              denominator   */
 tnom=1                               /*         temp nominator     */
 tden=0                               /*         temp denominator   */
 do While tnom<=high & tden<=high     /* nominator... not too large */
   n=trunc(x)                         /* take integer part of x     */
   z=tnom;                            /* save temp nominator        */
   tnom=n*tnom+nom;                   /* compute new temp nominator */
   nom=z                              /* assign nominator           */
   z=tden;                            /* save temp denominator      */
   tden=n*tden+den                    /* compute new temp denominato*/
   den=z                              /* assign denominator         */
   if n=x | tnom/tden=in then do
     if tnom>high | tden>high then    /* temp value(s) too large    */
       Leave                          /* don't use them             */
     nom=tnom                         /* otherwise take them as     */
     den=tden                         /* final values               */
     leave                            /* and end the loop           */
     end
   x=1/(x-n)                          /* compute x for next round   */
   end
 if den=1 then return nom             /* denominator 1: integer     */
               return nom'/'den       /* otherwise a fraction       */

</lang> Output:

  0.9054054054 67/74

  0.5185185185 14/27

  0.75 3/4

  0.905405400 693627417/766095958
> 0.9054053996 is different

  0.9054054054 67/74

  0.1428571428 1/7
> 0.1428571429 is different

  35.000 35

  35.001 35001/1000

  0.00000000001 0
                   not=?
> 0 is different

  0.000001000001 1/999999

Ruby

Works with: Ruby version 1.9+

Note that the decimal values of the task description are truncated in some cases.

This converts the string representation of the given values directly to fractions. <lang ruby>> '0.9054054 0.518518 0.75'.split.each { |d| puts "%s %s" % [d, Rational(d)] } 0.9054054 4527027/5000000 0.518518 259259/500000 0.75 3/4 => ["0.9054054", "0.518518", "0.75"]</lang>

Works with: Ruby version 1.9.2+

This loop finds the simplest fractions within a given radius, because the floating-point representation is not exact. <lang ruby>> [0.9054054, 0.518518, 0.75].each { |d| puts "%s %s" % [d, Rational(d).rationalize(0.0001)] } 0.9054054 67/74 0.518518 14/27 0.75 3/4 => [0.9054054, 0.518518, 0.75]</lang>

Works with: Ruby version 2.1.0+

A suffix for integer and float literals was introduced:

2.1.0p0 :001 > 0.9054054r
 => (4527027/5000000) 
2.1.0p0 :002 > 0.518518r
 => (259259/500000) 
2.1.0p0 :003 > 0.75r
 => (3/4) 

Seed7

The library bigrat.s7i defines the operator parse, which accepts, besides fractions, also a decimal number with repeating decimals.

<lang seed7>$ include "seed7_05.s7i";

 include "bigrat.s7i";

const proc: main is func

 begin
   writeln(bigRational parse "0.9(054)");
   writeln(bigRational parse "0.(518)");
   writeln(bigRational parse "0.75");
   writeln(bigRational parse "3.(142857)");
   writeln(bigRational parse "0.(8867924528301)");
   writeln(bigRational parse "0.(846153)");
   writeln(bigRational parse "0.9054054");
   writeln(bigRational parse "0.518518");
   writeln(bigRational parse "0.14285714285714");
   writeln(bigRational parse "3.14159265358979");
   writeln(bigRational parse "2.718281828");
   writeln(bigRational parse "31.415926536");
   writeln(bigRational parse "0.000000000");
 end func;</lang>
Output:
67/74
14/27
3/4
22/7
47/53
11/13
4527027/5000000
259259/500000
7142857142857/50000000000000
314159265358979/100000000000000
679570457/250000000
3926990817/125000000
0/1

Tcl

Works with: Tcl version 8.4+

Here is a complete script with the implemented function and a small test suite (which is executed when this script is called directly from a shell) - originally on http://wiki.tcl.tk/752: <lang Tcl>#!/usr/bin/env tclsh

proc dbl2frac {dbl {eps 0.000001}} {
  for {set den 1} {$den<1024} {incr den} {
     set num [expr {round($dbl*$den)}]
     if {abs(double($num)/$den - $dbl) < $eps} break
  }
  list $num $den
}
  1. -------------------- That's all... the rest is the test suite

if {[file tail $argv0] eq [file tail [info script]]} {

   foreach {test            -> expected} {

{dbl2frac 0.518518} -> {42 81} {dbl2frac 0.75} -> {3 4} {dbl2frac 0.9054054} -> {67 74}

   } {

catch $test res if {$res ne $expected} { puts "$test -> $res, expected $expected" }

   }

}</lang> Running it shows one unexpected result, but on closer examination it is clear that 14/27 equals 42/81, so it should indeed be the right solution:

~ $ fractional.tcl
dbl2frac 0.518518 -> 14 27, expected 42 81
~ $