Mixed numbers in Ruby / Rails

When doubling a recipe, for instance, 1 1/4 cups would become 2 1/2 cups.

These are both mixed numbers, a combination of a whole number and a fraction. We show them to users instead of their fraction equivalents, 5/4 and 5/2.

Rails provides no built-in methods for doing calculations with mixed numbers, so we have to convert them to and from rationals before treating them as numbers.

A rational in Ruby is a number that can be expressed as a fraction. In most cases, all of the quantities in an app can be expressed as rationals.

That way, we can perform calculations and still show users pretty fractions (i.e. 2 1/2 instead of 5/2).

Convert a mixed number to a rationalfor calculations

Unlike mixed numbers, rationals can be used in calculations. Use this function to convert a mixed number string into a rational.

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

def is_rational?(object)

trueifRational(object)rescue false

end

def mixed_number_to_rational(amount)

rational_to_return=0

amount.split(" ").each{|string|

ifis_rational?(string)# Number?

ifstring.include?("/")# Fraction?

rational_to_return+=Rational(string)

elsif string.to_i==string.to_f# Whole number?

rational_to_return+=string.to_i

elsif string.include?(".")# Decimal?

rational_to_return+=Rational(string)

else# Not a fraction, decimal, or whole number.

returnfalse

end

else

returnfalse# Not a number.

end

}

rational_to_return

end

mixed_number_to_rational("1 1/2")

=>3/2

mixed_number_to_rational("1/2")

=>1/2

mixed_number_to_rational("1")

=>1

mixed_number_to_rational("0.5")

=>1/2

mixed_number_to_rational("0.5").class

=>Rational<;Numeric

Above, we split a mixed number into its integer and fraction components and add them together to form a rational (or whole number). The function can also handle decimals, integers, and regular fractions.

Note: This is not ready for negative mixed numbers.

Convert a rational to a mixed numberfor the user

Use this method to convert a rational, like 3/2, into a mixed number that we can show to the user, like 1 1/2.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

def numeric_to_mixed_number(amount)

amount_as_integer=amount.to_i

if(amount_as_integer!=amount.to_f)&amp;&amp;(amount_as_integer>0)

fraction=amount-amount_as_integer

"#{amount_as_integer} #{fraction}"

else

amount.to_s

end

end

numeric_to_mixed_number(Rational(3,2))

=>"1 1/2"

numeric_to_mixed_number(Rational(1,2))

=>"1/2"

numeric_to_mixed_number(1)

=>"1"

Above, we convert to a mixed number if the provided number is not an integer and it’s more than 1. Otherwise, we return the number as a string.

A note on precision

Rationals and integers are exact, while decimals and floats are sometimes not. 1/3 is precise, but 1.333333…. is not.