website/content/blog/2015-11-15-fractions-js.md

7.4 KiB
Raw Blame History

id title date author layout guid aliases permalink medium_post mf2_cite tumblr_post_id kind tags
398 Math with Fractions.js 2015-11-15T12:46:14+00:00 Brandon Rozek post https://brandonrozek.com/?p=398
/2015/11/fractions-js/
/2015/11/fractions-js/
O:11:"Medium_Post":11:{s:16:"author_image_url";N;s:10:"author_url";N;s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";N;s:2:"id";N;s:21:"follower_notification";N;s:7:"license";N;s:14:"publication_id";N;s:6:"status";N;s:3:"url";N;}
O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"dd3b15d9d3c9";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:68:"https://medium.com/@brandonrozek/math-with-fractions-js-dd3b15d9d3c9";}
O:11:"Medium_Post":11:{s:16:"author_image_url";s:74:"https://cdn-images-1.medium.com/fit/c/200/200/1*dmbNkD5D-u45r44go_cf0g.png";s:10:"author_url";s:32:"https://medium.com/@brandonrozek";s:11:"byline_name";N;s:12:"byline_email";N;s:10:"cross_link";s:2:"no";s:2:"id";s:12:"dd3b15d9d3c9";s:21:"follower_notification";s:2:"no";s:7:"license";s:19:"all-rights-reserved";s:14:"publication_id";s:2:"-1";s:6:"status";s:6:"public";s:3:"url";s:68:"https://medium.com/@brandonrozek/math-with-fractions-js-dd3b15d9d3c9";}
a:1:{s:6:"author";a:0:{}}
a:1:{s:6:"author";a:0:{}}
135657739439
135657739439
article
JS

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 didnt 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 its reciprocal.

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

Simplification

Sometimes with the operations above, itll 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 Eucliders 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 its 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 '63/100' 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. Its important to look at different implementations and see which matches your needs better. This post isnt meant to go fully into detail of the library. To know what else the library can do, visit the readme page. If youre curious in how its implemented, check out the code. Email me if you have any questions/criticisms 🙂