At Cook Smarts, we’re going to let people scale recipes.

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 rational
**

*for 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) true if Rational(object) rescue false end def mixed_number_to_rational(amount) rational_to_return = 0 amount.split(" ").each { |string| if is_rational?(string) # Number? if string.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. return false end else return false # 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 number
**

*for 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) && (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.

Never mix decimals and fractions if you want to maintain precision.