-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d755c48
Showing
6 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Roman Shtylman <shtylman@gmail.com> | ||
Vadim Graboys <dimva13@gmail.com> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": "*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--ui qunit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
}); | ||
*/ |