# Supporting fractions in Ruby/Rails

Unicode comes with a selection of common single-character fractions like ½, ¼, and even ⅐ (see en.wikipedia.org/wiki/Number_Forms for more), but I need to store and display somewhat arbitrary fractions in a Rails app and it isn’t something I’ve had to do before so I’m documenting my workings.

Ruby has the `Rational` class that makes it easy to work with fractions and can perform a specific calculation I need (½ + ⅐ = ⁹⁄₁₄):

``````a = Rational(1, 2)
# => (1/2)

b = Rational(1, 7)
# => (1/7)

a + b
# => (9/14)
``````

My numbers are stored in a PostgreSQL database which doesn’t support rationals but fortunately in Ruby any number can be turned into a `Rational` with `#to_r`:

``````0.5.to_r
# => (1/2)
``````

Unfortunately you don’t often get a sensible fraction:

``````(9.0 / 14).to_r
# => (5790342378047781/9007199254740992)
``````

A `Rational` can be simplified with `Rational#rationalize` and with a bit of trial and error I determined that to arrive at ⁹⁄₁₄ I need to store the number with 3 decimal places and pass a magic number of ~0.005 to `Rational#rationalize`:

``````0.643.to_r
# => (2895814560399229/4503599627370496)

0.643.to_r.rationalize(0.1)
# => (2/3)

0.643.to_r.rationalize(0.01)
# => (7/11)

0.643.to_r.rationalize(0.001)
# => (9/14) ✅

# Can I store the number with two decimal places? No.
0.64.to_r.rationalize(0.001)
# => (16/25)

# What magic number do I need?
0.643.to_r.rationalize(0.005)
# => (9/14) ✅
``````

Next, I can never remember how to use decimals with Rails/PostgreSQL. The following Rails migration adds a decimal type column that can store numbers up to 999.999 — `precision: 6` means the number can have total of 6 digits, `scale: 3` means 3 of those digits come after the decimal point (surely it should be the other way round, scale for how big the number is and precision for its decimal places?):

``````add_column :standings, :points, :decimal, precision: 6, scale: 3
``````

Now that I’m storing the number with an appropriate fidelity and can turn it into a sensible fraction I want it to look nice. The following Rails helper method determines if a number is a fraction and formats it with `<sup>`/`<sub>` tags:

``````module ApplicationHelper
def fraction(numerator, denominator)
capture do
concat tag.sup(numerator)
concat '⁄' # Unicode fraction slash.
concat tag.sub(denominator)
end
end

def points(number)
return '0' if number.zero?

rational = number.to_r.rationalize(0.005)
whole, remainder = rational.numerator.divmod(rational.denominator)

capture do
concat whole.to_s unless whole.zero?
concat fraction(remainder, rational.denominator) unless remainder.zero?
end
end
end
``````

Storing approximate values in the database means you might have to be careful about losing precision, for example ⅐ × 49 = 7 but 0.143 × 49 ≠ 7:

``````BigDecimal('0.143').to_r.rationalize(0.005) * 49
# => (7/1)

BigDecimal('0.143') * 49
# => 0.7007e1

(BigDecimal('0.143') * 49).to_r.rationalize(0.005)
# => (589/84)
``````

Luckily I perform limited calculations on my data so this isn’t something I have to worry about.

## Bonus: Unicode fractions

I happened upon a site that demonstrates how to construct fractions from Unicode superscript and subscript characters. Even better is that the source is on GitHub so I borrowed the approach and implemented it in Ruby/Rails:

``````module ApplicationHelper
SUB = %w(₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉)
SUP = %w(⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹)

def fraction(numerator, denominator)
numerator.digits.reverse.map { |char| SUP.fetch(char) }.join \
+ '⁄' \ # Unicode fraction slash.
+ denominator.digits.reverse.map { |char| SUB.fetch(char) }.join
end
end
``````