Merge pull request #259 from BigFav/bigSin

Initial BigNumber sin support. Tests added as well.
This commit is contained in:
Jos de Jong 2015-01-04 14:04:32 +01:00
commit 622d1bd083
3 changed files with 113 additions and 42 deletions

View File

@ -12,7 +12,9 @@ module.exports = function (math) {
isBoolean = util['boolean'].isBoolean,
isComplex = Complex.isComplex,
isUnit = Unit.isUnit,
isCollection = collection.isCollection;
isCollection = collection.isCollection,
bigSin = util.bignumber.sin;
/**
* Calculate the sine of a value.
@ -37,8 +39,8 @@ module.exports = function (math) {
*
* cos, tan
*
* @param {Number | Boolean | Complex | Unit | Array | Matrix | null} x Function input
* @return {Number | Complex | Array | Matrix} Sine of x
* @param {Number | BigNumber | Boolean | Complex | Unit | Array | Matrix | null} x Function input
* @return {Number | BigNumber | Complex | Array | Matrix} Sine of x
*/
math.sin = function sin(x) {
if (arguments.length != 1) {
@ -72,9 +74,8 @@ module.exports = function (math) {
}
if (x instanceof BigNumber) {
// TODO: implement BigNumber support
// downgrade to Number
return sin(x.toNumber());
// TODO: implement better BigNumber support
return bigSin(x);
}
throw new math.error.UnsupportedTypeError('sin', math['typeof'](x));

View File

@ -39,33 +39,6 @@ exports.phi = function (precision) {
return new Big(1).plus(new Big(5).sqrt()).div(2);
};
/**
* Calculate the arc tangent of x
*
* arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
* = x - x^2*x^1/3 + x^2*x^3/5 - x^2*x^5/7 + x^2*x^7/9 - ...
*
* @param {BigNumber} x
* @returns {BigNumber} arc tangent of x
*/
exports.arctan = function (x) {
var y = x;
var yPrev = NaN;
var x2 = x.times(x);
var num = x;
var sign = -1;
for (var k = 3; !y.equals(yPrev); k += 2) {
num = num.times(x2);
yPrev = y;
y = (sign > 0) ? y.plus(num.div(k)) : y.minus(num.div(k));
sign = -sign;
}
return y;
};
/**
* Calculate BigNumber pi.
*
@ -171,6 +144,33 @@ exports.and = function(x, y) {
return bitwise(x, y, function (a, b) { return a & b });
};
/**
* Calculate the arc tangent of x
*
* arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
* = x - x^2*x^1/3 + x^2*x^3/5 - x^2*x^5/7 + x^2*x^7/9 - ...
*
* @param {BigNumber} x
* @returns {BigNumber} arc tangent of x
*/
exports.arctan = function (x) {
var y = x;
var yPrev = NaN;
var x2 = x.times(x);
var num = x;
var sign = -1;
for (var k = 3; !y.equals(yPrev); k += 2) {
num = num.times(x2);
yPrev = y;
y = (sign > 0) ? y.plus(num.div(k)) : y.minus(num.div(k));
sign = -sign;
}
return y;
};
/*
* Special Cases:
* n << -n = N
@ -281,6 +281,60 @@ exports.or = function (x, y) {
return bitwise(x, y, function (a, b) { return a | b });
};
/**
* Calculate the sine of x using Taylor Series.
*
* sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ...
* = x - x^2*x^1/3! + x^2*x^3/5! - x^2*x^5/7! + x^2*x^7/9! - ...
*
* TODO: Replace with Chebyshev approximation.
*
* @param {BigNumber} x
* @returns {BigNumber} sine of x
*/
exports.sin = function (x) {
var BigNumber = x['constructor'];
var precision = BigNumber['precision'];
if (x.isNaN() || !x.isFinite()) {
return new BigNumber(NaN);
}
// Get number's offset within the period of sin (tau)
var pi = exports.pi(2 * precision - 5);
var tau = pi.times(2);
// Catch if tau multiple using pi's precision
if (x.div(pi.toDP(x.dp(), 1)).toNumber() % 2 == 0) {
return new BigNumber(0);
}
var y = x.mod(tau);
// Catch if tau multiple with tau's precision
if (y.toDP(x.dp(), 1).isZero()) {
return new BigNumber(0);
}
if (y.gt(pi)) {
y = y.minus(tau);
}
y['constructor']['precision'] = precision;
var yPrev = NaN;
var y2 = y.times(y);
var num = y;
var den = BigNumber['ONE'];
var sign = -1;
for (var k = 1; !y.equals(yPrev); k += 2) {
num = num.times(y2);
den = den.times(k+1).times(k+2);
yPrev = y;
y = (sign > 0) ? y.plus(num.div(den)) : y.minus(num.div(den));
sign = -sign;
}
return y.toDP(precision - 1);
};
/*
* Special Cases:
* n >> -n = N

View File

@ -20,20 +20,36 @@ describe('sin', function() {
it('should return the sine of a number', function() {
approx.equal(sin(0), 0);
approx.equal(sin(pi*1/4), 0.707106781186548);
approx.equal(sin(pi*1/8), 0.382683432365090);
approx.equal(sin(pi*2/4), 1);
approx.equal(sin(pi/8), 0.382683432365090);
approx.equal(sin(pi/4), Math.SQRT2/2);
approx.equal(sin(pi/2), 1);
approx.equal(sin(pi*3/4), 0.707106781186548);
approx.equal(sin(pi*4/4), 0);
approx.equal(sin(pi), 0);
approx.equal(sin(pi*5/4), -0.707106781186548);
approx.equal(sin(pi*6/4), -1);
approx.equal(sin(pi*3/2), -1);
approx.equal(sin(pi*7/4), -0.707106781186548);
approx.equal(sin(pi*8/4), 0);
approx.equal(sin(pi/4), math.sqrt(2)/2);
approx.equal(sin(pi*2), 0);
});
it('should return the sine of a bignumber (downgrades to number)', function() {
approx.equal(sin(math.bignumber(1)), 0.841470984807897);
it('should return the sine of a bignumber', function() {
var bigmath = math.create({number: 'bignumber', precision: 60});
assert.deepEqual(sin(math.bignumber(0)), math.bignumber(0));
assert.deepEqual(sin(math.bignumber(1)), math.bignumber('0.84147098480789650665250232163029899962256306079837106567275171'));
bigmath.config({precision: 15});
var bigPi = bigmath.pi;
var result = bigmath.SQRT2.div(2).toString();
assert.deepEqual(bigmath.sin(bigPi.div(4)).toString(), result);
assert.deepEqual(bigmath.sin(bigPi.div(2)).toString(), '1');
assert.deepEqual(bigmath.sin(bigPi.times(3).div(4)).toString(), result);
assert.deepEqual(bigmath.sin(bigPi).toString(), '0');
assert.deepEqual(bigmath.sin(bigPi.times(5).div(4)).toString(), '-'+result);
assert.deepEqual(bigmath.sin(bigPi.times(3).div(2)).toString(), '-1');
assert.deepEqual(bigmath.sin(bigPi.times(7).div(4)).toString(), '-'+result);
assert.deepEqual(bigmath.sin(bigPi.times(2)).toString(), '0');
assert.deepEqual(bigmath.sin(bigmath.tau).toString(), '0');
assert.deepEqual(bigmath.sin(bigmath.tau.times(2)).toString(), '0');
});
it('should return the sine of a complex number', function() {