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 < 0) a *= -1;
if (b < 0) b *= -1;
if (a < 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 < other;
};
Rational.prototype.isGreaterThan = function(other) {
return this > other;
};
Rational.prototype.isEqualTo = function(other) {
return this.top === other.top && 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 < 0 && this.bottom < 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 < 0 && this.top >= 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
orother * 6
, I implicitly callvalueOf()
. Is that okay? Should I make it explicit (to me, sayingthis * 5
is clear: multiplythis
by 5), but I'm not sure in general. - Same as the question above for
this < other
andthis > other
. - Can I refactor my
GCF
andLCM
algorithms to take into account negative numbers so that I don't have to handle them as special cases in thesimplify
function? Right now, they simply make everything positive and return a positive number.