Math with Fractions.js

Brandon Rozek

November 15, 2015

Last week I published my first library over on Github called Fractions.js. Fractions.js is a library to help avoid the mathmatetical errors in floating point arithmetic. What do you mean by floating point artihmetic errors? Here is an example: .1 * .2 outputs 0.020000000000000004 even though the correct answer is .02

Purpose

My team and I are currently working on a new game for Math I/O. One of the levels is testing whether the player can add, subtract, multiply, and divide fractions correctly. I didn’t want to implement a solution where we check if the input entered is within a range of the answer. So I asked the team, how do you add, subtract, multiply, and divide fractions in your head? As we were working through it together, I realized that this is a common problem over at Math I/O. So I decided to make a library dedicated to this.

How it works

I broke up each fraction into two things, a numerator and a denominator. With these two numbers, I can figure out all of the most common operations.

Addition

For addition, if two fractions have the same denominator, then you just need to add the numerators.

1/3 + 1/3 = 2/3

If not, then you need to change it to have a common denominator. We can do this by multiply each fractions numerator and denominator by the other fractions denominator.

1/2 + 1/3 = (1 * 3)/ (2 * 3) + (1 * 2)/ (2 * 3) = 3/6 + 2/6 = 5/6

Subtraction

Works the same as addition, except the second fraction is subtracted (taken away) from the first.

Multiplication

To multiply two fractions, just multiply the numerators by each other, and the denominators by each other.

2/3 * 1/2 = 2/6

Division

Treated similar to multiplication since dividing a number is the same thing as multiplying by it’s reciprocal.

1 / (1 / 2) = 1 * 2 = 2

Simplification

Sometimes with the operations above, it’ll produce fractions in an unsimplified form. To avoid any confusion, I created a simplify function. It was challanging trying to figure out a way to code this. While I was browsing around for an implementation, I knocked into Euclider’s algorithm for finding the greatest common factor. Straight from the Wikipedia article (where a is greater than b):

function gcd(a, b)
    while b ≠ 0
       t := b;
       b := a mod b;
       a := t;
    return a;

I can then simplify the fraction by dividing the numerator and denominator by the greatest common factor.

The API

I decided to provide as much flexibility as I can in the API. You have several ways to create a new Fraction.


var oneHalf = new Fraction(1,2);
var oneHalf = new Fraction(.5);
var oneHalf = new Fraction("1/2");
var oneHalf = new Fraction("1", "2")

All of these results will return a Fraction with a numerator of 1 and a denominator of 2. You also have two different ways to do the most common operations.


var fiveThirds = Fraction.add("1/3", "4/3");
var fiveThirds = new Fraction("1/3").add("4/3");

The second style came from how jQuery implements it’s library. That way you can chain operations.


new Fraction("1/2").add("2/3").divide("5/6").subtract("7/8").multiply("6/5").toString()

Outputs 63100 This is accomplished in the code through prototypes.


Fraction.add = function(frac1, frac2) {
    Fraction.correctArgumentLength(2, arguments.length);
    frac1 = Fraction.toFraction(frac1)
    frac2 = Fraction.toFraction(frac2)

    var newFrac = frac1;
    newFrac.numerator = frac1.numerator * frac2.denominator + frac1.denominator * frac2.numerator;
    newFrac.denominator = frac1.denominator * frac2.denominator;
    return Fraction.simplify(newFrac);
}
Fraction.prototype.add = function(frac) {
    Fraction.correctArgumentLength(1, arguments.length);
    return Fraction.change(this, Fraction.add(this, frac));
}

In the code, the add prototype calls the Fraction.add function within it to avoid code duplication.

Conclusion

After I coded this up, I looked online for different implementations and found fraction.js by Erik Garrison. It’s important to look at different implementations and see which matches your needs better. This post isn’t meant to go fully into detail of the library. To know what else the library can do, visit the readme page. If you’re curious in how it’s implemented, check out the code. Email me if you have any questions/criticisms 🙂