repl.it
@t3m2/

Converts rational numbers in decimal notation to fractions

Python

This includes repeating decimals! :) Read the general comment for more infomation.

fork
loading
Files
  • main.py
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""
Author: @t3m2.
Date of creation: 09/07/2019, , dd/mm/yyyy, (july).
Version: 14/12/2019, dd/mm/yyyy, (december).
Language: Python.
This program defines three functions which can be used to
converts rational numbers into fractions:
from_string_to_fraction(),
gcd() and number_of_decimal_places_of().
I have built the function from_string_to_fraction()
that receives a string representing a rational number
in decimal notation (including repeating decimals),
such as "2.5(37)", "-7.8" or "4"
and returns a string with the correspondent fraction
in form "n/m" where n and m are integers.
Eg.: from_string_to_fraction("-2.5(37)") returns "-1256/495". 
If the number is a repeating decimal,
its peridod should appear between round brackets.
It needs the functions gcd() and number_of_decimal_places_of()!
Read the functions' docstrings for more information.
"""
__author__ = "t3m2"
__date__ = "09/07/2019, , dd/mm/yyyy, (july)"
__version__ = "14/12/2019, dd/mm/yyyy, (december)"


def gcd(a=1, b=1):
  """Returns the greatest common divisor
  between two positive integers. 
  (Recursive solution)
  
  Make sure a and b are positive int's 
  because this function assumes that."""
  if b == 0:
    return a 
  else: 
    return gcd(b, a%b)


def number_of_decimal_places_of(x=0):
  """Returns the number of decimal places of a float or int.
  
  Make sure x is a float or int
  because this function assumes that."""
  if x == int(x):
    return 0
  return len(str(x)[str(x).index('.')+1:])


def from_string_to_fraction(x='0'):
  """Receives a string representing a rational number in decimal notation,
  (including repeating decimals) such as "-2.5(37)", "-7.8" or "4", and
  returns a string with the correspondent fraction in form "n/m",
  where both n and m are integers. 
  
  Eg.: from_string_to_fraction("-2.5(37)") returns "-1256/495".
  
  It needs the functions gcd() and number_of_decimal_places_of()!
  
  This only works with rational numbers because rational numbers
  are all the numbers and the only numbers that can be written as
  the divison of two integers, that's the definition of rational numbers.
  Note that recurring decimals are rational numbers, and that 0,(9)=1.
  
  Make sure that: (input restrictions)
  - the argument is a valid string representing
    a rational number in decimal notation;
  - the decimal separator (if there) is a '.' or a ',';
  - if the input is a recurring decimal, the period comes between round brackets.
    (12.431111111111111... is represented as "12.43(1)".)
  Because this function assumes that!"""
  
  # The input string can have a ',' or a '.' separating the int and the decimal part:
  x = x.replace(',', '.', 1)  
  
  sign = 1 
  if x[0] == '-':  
    # iff x represent a negative number, this turns x into
    # a string representing positive number,
    # because if it easier to work with positives numbers.
    # And, in the end, we turn the result into negative again
    # by making something like: "final_result*=sign".
    sign = -1
    x = x[1:]

  ### Getting the finit part (f) and the period (p): ###

  # I will explain this with an example:
  # If x == "2.5(37)"; then I set f, the finit part, to 2.5 and p, the period, to 37.
  # If the number is non-recurring, f = x, since it has no period.
  # Eg: if x == "-3.4"; then f = -3.4 and p = 0.
  # Note that x, our argument, is still a 'string'.

  try:  # This will be executed iff x is a non-recurring decimal:
    f = eval(x)  # eval(x) "turns" the string x into a float or int.
    p = 0
  except TypeError:  # This will be executed iff x is a recurring decimal:
    f = float(x[:x.index('(')])  # The finit part of the dizim is all the way until '('.

    p = int(x[x.index('(')+1:x.index(')')])  # The period of the dizim is 
    # the part of the number between '(' and ')').

  ### Getting the numerator and denominator: ###

  # With f and p defined, I have to discover the numerator and the denominator:
  
  # Here is a method that can be used in order to discover the fraction:
  # If y=2,5(37): (mathematical notation)
  # 1000y - 10y = 2537,(37) - 25,(37)     <=>
  # 1000y - 10y = 2537 - 25               <=>
  # (1000-10) * y = 2537 - 25             <=>
  # <=> y = (2537-25)  /   (1000-10)      <=>
  # <=> y =  2512      /     990           =>
  #  => y = numerator  /    denominator    => # Then we need to simplify the fraction,
  #  => y =  1256      /     495              # and this will be the final result.
  
  # Note that both numerator and denominator are integers.

  # I implemented this with f and p:

  numerator = f*10**(number_of_decimal_places_of(f)+len(str(p)))+p \
              - f*10**number_of_decimal_places_of(f) 

  denominator = 10**(number_of_decimal_places_of(f)+len(str(p))) \
                - 10**number_of_decimal_places_of(f)

  ### Simplifying the fraction: ###

  # Here I am slimplifying the fraction, if it's possible:

  factor = gcd(numerator, denominator)

  # "sign*" is used to get the correct sign of the final answer, 
  # ie, the same sign of x.
  numerator = sign*(numerator/factor)
  denominator = denominator/factor

  return "%d/%d" % (numerator, denominator)
  

### TESTING ###

print("This program turns \"any\" rational number in decimal notation \
into a fraction, for example: -2.5(37) = -2.537373737373737... = -1256/495\n\n")

while 1:
  try:
    x = input("Enter a rational number in decimal notation (exit: 'b'): ")
    if x == 'b':
      break
    print("%s = %s" % (x, from_string_to_fraction(x)))
  except:
    print("Error: probably, invalid input.")

  print()
Fetching token
?