Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
defunctzombie committed May 1, 2012
0 parents commit d755c48
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Roman Shtylman <shtylman@gmail.com>
Vadim Graboys <dimva13@gmail.com>
214 changes: 214 additions & 0 deletions num.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
var int = require('int');

function Num(num, precision) {
if (!(this instanceof Num)) {
return new Num(num, precision);
}

var self = this;
if (num instanceof Num) {
self._int = int(num._int);
self._precision = num._precision;
return self;
}

// convert to a string
num = '' + num;

// find Num point
var dec = num.indexOf('.');

if (dec >= 0) {
// take out the Num point
num = num.replace('.', '');
var precision = num.length - dec;
}
else {
var precision = 0;
}

this._int = int(num);
this._precision = precision;
}

// TODO: this is probably slow, make faster
Num.prototype.toString = function() {
var num_str = this._int.toString();

var sign = '';
if (num_str.charAt(0) === '-') {
sign = '-';
}

if (this._precision === 0) {
return num_str;
}

var arr = num_str.split('');
arr.splice(arr.length - this._precision, 0, '.');
return sign + arr.join('');
};

Num.prototype.valueOf = Num.prototype.toString;

/// return {int} the precision
Num.prototype.get_precision = function() {
return this._precision;
};

/// setting precision to < current precision will floor, NOT round
/// modifies this object
Num.prototype.set_precision = function(precision) {
var self = this;
var precision_diff = precision - self._precision;

if (precision_diff > 0) {
self._int = self._int.mul(int(10).pow(precision_diff));
}
else if(precision_diff < 0) {
self._int = self._int.div(int(10).pow(-precision_diff));
}

self._precision += precision_diff;
return self;
};

/// returns a copy
Num.prototype.round = function(precision) {
var copy = Num(this);

if (precision >= copy._precision) {
return copy;
}

var num_str = copy._int.toString();

// the index to check for rounding
var idx = num_str.length - copy._precision + precision;

// the number to check if >= 5
var n = num_str[idx] - 0;

var neg = num_str[0] === '-';

copy.set_precision(precision);

if ((neg && n < 5) || (!neg && n >= 5)) {
copy._int = copy._int.add(1);
}

return copy;
};

/// returns new Num, -this
Num.prototype.neg = function() {
return new Num(this._int.neg(), this._precision);
};

Num.prototype.to_int = function(precision) {
var copy = this.copy();
copy.set_precision(precision);
return copy._int;
};

/// converts a Num to an integer representation with specified precision
/// Num('32.1').to_int(5) will be converted to 3210000
/// for doing fast calculations before converting back with Num.from_int
/// warning! cannot convert more than ~16 total Num digits of precision
Num.prototype.to_int = function(precision) {
var dec_str = this.round(precision).toString();
return Math.round(dec_str * Math.pow(10, precision));
};

/// returns a + b
/// a, b can each be either a Num, String, or Number
/// will return a new Num with the greatest precision of the operands
Num.add = function(a, b) {
// force a,b to be Num
if(!(a instanceof Num))
a = Num(a);
if(!(b instanceof Num))
b = Num(b);

// make sure num and a have the same precision
if(a._precision < b._precision) {
a = a.copy(); // copy before modifying
a.set_precision(b._precision);
} else if(b._precision < a._precision) {
b = b.copy(); // copy before modifying
b.set_precision(a._precision);
}

// the integer result
var num_res = a._int.add(b._int);

return new Num(num_res, a._precision);
};

/// returns a - b
/// a, b can each be either a Num, String, or Number
/// will return a new Num with the greatest precision of the operands
Num.sub = function(a, b) {
b = Num(b); // convert/copy before modifying
b._int = b._int.neg(); // negate
return Num.add(a, b);
};

/// returns a * b
/// a, b can each be either a Num, String, or Number
/// will return a new Num with precision = sum(a.precision,b.precision)
Num.mul = function(a, b) {
// force a,b to be Num
if(!(a instanceof Num))
a = Num(a);
if(!(b instanceof Num))
b = Num(b);

return new Num(a._int.mul(b._int), a._precision + b._precision);
};

/// returns < 0 if a < b, 0 if a == b, > 0 if a > b
Num.cmp = function(a, b) {
// force a,b to be Num
if(!(a instanceof Num))
a = Num(a);
if(!(b instanceof Num))
b = Num(b);

return a.sub(b).toNumber();
};

Num.eq = function(a, b) {
return Num.cmp(a, b) === 0;
};

Num.gt = function(a, b) {
return Num.cmp(a, b) > 0;
};

Num.gte = function(a, b) {
return Num.cmp(a, b) >= 0;
};

Num.lt = function(a, b) {
return Num.cmp(a, b) < 0;
};

Num.lte = function(a, b) {
return Num.cmp(a, b) <= 0;
};

// add all the static methods in Num to Num's prototype
(function() {
function add_method(name) {
Num.prototype[name] = function(b) {
return Num[name](this, b);
}
}

for(var i in Num) {
add_method(i);
}
})();

module.exports = Num;
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"author": "Roman Shtylman <shtylman@gmail.com>",
"name": "num",
"description": "arbitrary precision integer and decimal library in pure javascript",
"version": "0.0.0alpha",
"repository": {
"type": "git",
"url": "git://github.com/shtylman/node-num.git"
},
"main": "num.js",
"dependencies": {
"int": "0.0.1"
},
"devDependencies": {
"mocha": ">=1.x.x"
},
"optionalDependencies": {},
"engines": {
"node": "*"
}
}
1 change: 1 addition & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--ui qunit
143 changes: 143 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@

var assert = require('assert');
var num = require('../');

test('build', function() {
// zeros
assert.equal(num(0), '0');
assert.equal(num('0'), '0');
assert.equal(num('0.'), '0');
assert.equal(num('.0'), '0.0');
assert.equal(num('-.00'), '0.00');
assert.equal(num('0.000'), '0.000');

// whole number
assert.equal(num(5), '5');
assert.equal(num(-5), '-5');
assert.equal(num('5'), '5');
assert.equal(num('-5'), '-5');

// misc
assert.equal(num(1.2), '1.2');
assert.equal(num(.2), '0.2');
assert.equal(num(-.2), '-0.2');
assert.equal(num(-1.2), '-1.2');
assert.equal(num('0.001'), '0.001');
assert.equal(num('0.001234'), '0.001234');
assert.equal(num(123), '123');
assert.equal(num(-234), '-234');

// large numbers
assert.equal(num('987654321987654321'), '987654321987654321');
assert.equal(num('-987654321987654321.12345678901'), '-987654321987654321.12345678901');
assert.equal(num('987654321987654321.12345678901'), '987654321987654321.12345678901');
assert.equal(num('-.000098765432198765432112345678901'), '-0.000098765432198765432112345678901');
});

test('add', function() {
assert.equal(num.add(0, 0), '0');
assert.equal(num.add(-0, 0.0), '0');

// presers precision
assert.equal(num.add(1.2, -1.2), '0.0');

// misc
assert.equal(num.add(1.2, 2.4), '3.6');

// large numbers
assert.equal(num.add('987654321987654321.12345678901', 1000.012), '987654321987655321.13545678901');

assert.equal(num.add('987654321987654321.12345678901', '-987654321987654321.12345678901'), '0.00000000000');
});

test('sub', function() {
assert.equal(num.sub(0, 0), '0');
assert.equal(num.sub('0', '-0'), '0');

assert.equal(num.sub('1.0', '-1.0'), '2.0');

assert.equal(num('987654321987654321.12345678901').sub(100.012), '987654321987654221.11145678901');
assert.equal(num(100.012).sub(num('987654321987654321.12345678901')), '-987654321987654221.11145678901');
});

test('mul', function() {
assert.equal(num.mul(1.2, 2.4), '2.88');
assert.equal(num.mul(.2, 2.4), '0.48');
assert.equal(num.mul(.2, 2), '0.4');
assert.equal(num.mul(5, 2), '10');
assert.equal(num.mul('123456789.32423455645', '123323.34343'), '15225104028597.7358269570716235');
});

test('precision', function() {
assert.equal(num(1.999).set_precision(30), '1.999000000000000000000000000000');
assert.equal(num(1.999).set_precision(1), '1.9');
assert.equal(num(-1.999).set_precision(30), '-1.999000000000000000000000000000');
assert.equal(num(-1.999).set_precision(1), '-2.0'); // TODO: is this correct behavior?
});

test('round', function() {
assert.equal(num(123.999).round(30), '123.999');
assert.equal(num(123.999).round(1), '124.0');
assert.equal(num(123.450).round(1), '123.5');
assert.equal(num(123.449).round(1), '123.4');
assert.equal(num(123.495).round(2), '123.50');
assert.equal(num(123.495).round(0), '123');
assert.equal(num(123.500).round(0), '124');

assert.equal(num(-123.999).round(30), '-123.999');
assert.equal(num(-123.999).round(1), '-124.0');
assert.equal(num(-123.450).round(1), '-123.5');
assert.equal(num(-123.449).round(1), '-123.4');
assert.equal(num(-123.495).round(2), '-123.50');
assert.equal(num(-123.495).round(0), '-123');
assert.equal(num(-123.500).round(0), '-124');

assert.equal(num(0.999).round(30), '0.999');
assert.equal(num(0.999).round(1), '1.0');
assert.equal(num(0.450).round(1), '0.5');
assert.equal(num(0.449).round(1), '0.4');
assert.equal(num(0.495).round(2), '0.50');
assert.equal(num(0.495).round(0), '0');

assert.equal(num(-0.999).round(30), '-0.999');
assert.equal(num(-0.999).round(1), '-1.0');
assert.equal(num(-0.450).round(1), '-0.5');
assert.equal(num(-0.449).round(1), '-0.4');
assert.equal(num(-0.495).round(2), '-0.50');
assert.equal(num(-0.495).round(0), '0');

assert.equal(num('0.000').round(0), '0');
assert.equal(num('0.000').round(1), '0.0');
});

/*
test('int_conv', function() {
assert.equal(num.from_int(0, 0), '0');
assert.equal(num.from_int(1, 0), '1');
assert.equal(num.from_int(-1, 0), '-1');
assert.equal(num.from_int(0, 5), '0.00000');
assert.equal(num.from_int(1, 5), '0.00001');
assert.equal(num.from_int(-1, 5), '-0.00001');
assert.equal(num.from_int(123456, 5), '1.23456');
assert.equal(num.from_int(-123456, 5), '-1.23456');
assert.equal(num('0').to_int(1), 0);
assert.equal(num('1').to_int(0), 1);
assert.equal(num('1').to_int(2), 100);
assert.equal(num('1.2345').to_int(5), 123450);
assert.equal(num('-1.2345').to_int(5), -123450);
});
test('get_precision', function() {
assert.equal(0, num('0').get_precision());
assert.equal(2, num('0.20').get_precision());
assert.equal(1, num('.5').get_precision());
});
test('to_int', function() {
var d1 = num('12.35');
assert.equal('1235', '' + d1.to_int(2));
assert.equal('1235000', '' + d1.to_int(5));
assert.equal('123', '' + d1.to_int(1));
});
*/

0 comments on commit d755c48

Please sign in to comment.