Rational class supporting precise numbers This is my JavaScript Rational class, similar to other languages' BigFraction class, but with one major difference:

This class doesn't support arbitrarily large rational numbers:  

i.e. new Rational(Number.MAX_VALUE + 1) may fail.
However, it supports arbitrarily precise numbers
i.e. new Rational(1, 10).add(new Rational(2, 10)) will be exactly new Rational(3, 10) instead of .3000000000004.

window.Rational = (function() {
  var GCF, LCM;

  // Internal GCF and LCM

  GCF = function(a, b) {
    var temp;
    if (a &lt; 0) a *= -1;
    if (b &lt; 0) b *= -1;
    if (a &lt; b) {
      temp = a;
      a = b;
      b = temp;
    }
    return a % b === 0 ? b : GCF(a % b, b);
  };

  LCM = function(a, b) {
    return Math.abs(a * b) / GCF(a, b);
  };

  function Rational(top, bottom) {
    this.top = top;
    // bottom defaults to 1
    this.bottom = bottom != null ? bottom : 1;
    this.simplify();
  }

  Rational.prototype.add = function(other) {
    var lcm = LCM(this.bottom, other.bottom);
    // If we multiply by lcm
    // it becomes an integer
    // but we round to prevent floating point innacuracies
    var top1 = Math.round(this * lcm);
    var top2 = Math.round(other * lcm);
    return new Rational(top1 + top2, lcm);
  };

  Rational.prototype.subtract = function(other) {
    return this.add(other.multiply(Rational.constants.NEGATIVE_ONE));
  };

  Rational.prototype.multiply = function(other) {
    return new Rational(this.top * other.top, this.bottom * other.bottom);
  };

  Rational.prototype.divide = function(other) {
    return this.multiply(other.reciprocal());
  };

  Rational.prototype.reciprocal = function() {
    return new Rational(this.bottom, this.top);
  };

  Rational.prototype.isLessThan = function(other) {
    return this &lt; other;
  };

  Rational.prototype.isGreaterThan = function(other) {
    return this &gt; other;
  };

  Rational.prototype.isEqualTo = function(other) {
    return this.top === other.top &amp;&amp; this.bottom === other.bottom;
  };

  Rational.prototype.valueOf = function() {
    return this.top / this.bottom;
  };

  Rational.prototype.simplify = function() {
    var gcf;
    if (this.top === 0) {
      // No need to calculate GCF because GCF = 0
      this.bottom = 1;
      return;
    }
    if (this.bottom === 0) {
      throw new Error("Cannot have denominator 0");
    }
    gcf = GCF(this.top, this.bottom);
    if (this.top &lt; 0 &amp;&amp; this.bottom &lt; 0) {
      // Since GCF doesn't handle negatives
      // if both numerator and denominators are negative
      // we can cancel the negative
      gcf *= -1;
    }
    // If the bottom is less than zero, then move the negative sign to the top
    if (this.bottom &lt; 0 &amp;&amp; this.top &gt;= 0) {
      this.top *= -1;
      this.bottom *= -1;
    }
    this.top /= gcf;
    this.bottom /= gcf;
    return this;
  };


  Rational.constants = {
    ZERO: new Rational(0),
    ONE: new Rational(1),
    NEGATIVE_ONE: new Rational(-1)
  };
  return Rational;

})();


Questions:


I think that by doing this * 5 or other * 6, I implicitly call valueOf(). Is that okay? Should I make it explicit (to me, saying this * 5 is clear: multiply this by 5), but I'm not sure in general.
Same as the question above for this &lt; other and this &gt; other.
Can I refactor my GCF and LCM algorithms to take into account negative numbers so that I don't have to handle them as special cases in the simplify function? Right now, they simply make everything positive and return a positive number.
Not a complete answer, but I noticed some bugs:

LCM = function(a, b) {
  return Math.abs(a * b) / GCF(a, b);
};


The computation of a * b will easily overflow (thus losing precision) for large numbers a, b. You can improve matters by rewriting it as

function LCM(a, b) {
  var g = a / GCF(a,b);
  return Math.abs(g * b);
}


I also used function LCM as opposed to LCM = function just as a stylistic preference. See here.

Similarly:

Rational.prototype.add = function(other) {
  var lcm = LCM(this.bottom, other.bottom);
  // If we multiply by lcm
  // it becomes an integer
  // but we round to prevent floating point innacuracies
  var top1 = Math.round(this * lcm);
  var top2 = Math.round(other * lcm);
  return new Rational(top1 + top2, lcm);
};


If I understand correctly, in the expression this * lcm, lcm is a Number, so you're basically doing this.valueOf() * lcm, which again wipes out all your pretended precision. You should avoid big numbers (which become floating-point), and you should definitely avoid unnecessary floating-point divisions.

Rational.prototype.add = function(other) {
  var lcm = LCM(this.bottom, other.bottom);
  var top1 = this.top * (lcm / this.bottom);
  var top2 = other.top * (lcm / other.bottom);

  return new Rational(top1 + top2, lcm);
};