Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logarithms #333

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 75 additions & 2 deletions bignumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
* isLessThanOrEqualTo lte | minimum min
* isNaN | random
* isNegative | sum
* isPositive |
* isPositive | euler e
* isZero |
* logBase log |
* naturalLog ln |
* minus |
* modulo mod |
* multipliedBy times |
Expand Down Expand Up @@ -74,7 +76,7 @@
* Create and return a BigNumber constructor.
*/
function clone(configObject) {
var div, convertBase, parseNumeric,
var div, log, convertBase, parseNumeric,
P = BigNumber.prototype = { constructor: BigNumber, toString: null, valueOf: null },
ONE = new BigNumber(1),

Expand Down Expand Up @@ -808,6 +810,27 @@
return sum;
};

/*
* Returns e to dp decimal places
* Runtime could be improved with binary splitting at expense of storage
* Runtime of 5,000 digits was ~3s
*/

BigNumber.e = BigNumber.euler = function (dp, rm) {
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
let oldDp = DECIMAL_PLACES
dp = dp | oldDp
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
BigNumber.set({DECIMAL_PLACES: dp + 1})
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
let e = BigNumber('1')
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
let divisor = BigNumber('1')
let one = BigNumber('1')
for (let i = 2; i < dp * 10; i++) {
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
e = e.plus(one.div(divisor))
divisor = divisor.multipliedBy(BigNumber(i))
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
}
e = BigNumber(round(e, dp, rm, true).toString())
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
BigNumber.set({DECIMAL_PLACES: oldDp})
return BigNumber(round(e, dp, rm, true).toString())
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
}

// PRIVATE FUNCTIONS

Expand Down Expand Up @@ -1223,6 +1246,44 @@
})();


// Perform logorithm using the method by Daniel Shanks https://www.ams.org/journals/mcom/1954-08-046/S0025-5718-1954-0061464-9/S0025-5718-1954-0061464-9.pdf
log = (function () {
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
// x: base, y: number
return function (x, y, dp, rm) {
if (x.comparedTo(0) + y.comparedTo(0) !== 2 || !x.isFinite() || !y.isFinite() || y.valueOf() === '1') {
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
return BigNumber(NaN)
}
var A = y
var B = x
var C = BigNumber(1)
var D = BigNumber(0)
var E = BigNumber(0)
var F = BigNumber(1)
var s = 1
var one = BigNumber('1')
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
if (A.comparedTo(one) === -1) {
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
A = one.div(A)
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
s *= -1
}
if (B.comparedTo(one) === -1) {
B = one.div(B)
s *= -1
}
for (let i = 0; i < dp * 1.1 + 5; i) {
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
if (A.comparedTo(B) > -1 && B.valueOf() !== '1') {
[A, C, D] = [A.div(B), C.plus(E), D.plus(F)]
} else if (B.valueOf() === '1') {
break
} else {
[A, B, C, D, E, F] = [B, A, E, F, C, D]
i++
}

}
return round(E.div(F).multipliedBy(s), dp, rm)
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
};
})();

/*
* Return a string representing the value of BigNumber n in fixed-point or exponential
* notation rounded to the specified decimal places or significant digits.
Expand Down Expand Up @@ -1669,6 +1730,18 @@
};


/*
* Return a new BigNumber whose value is the value of log with base this BigNumber of the value
* BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE
*/
P.logBase = P.log = function (y, b) {
return log(this, new BigNumber(y, b), DECIMAL_PLACES, ROUNDING_MODE)
}

P.naturalLog = P.ln = function () {
return log(this, BigNumber.euler(DECIMAL_PLACES), DECIMAL_PLACES, ROUNDING_MODE)
MikeMcl marked this conversation as resolved.
Show resolved Hide resolved
}

/*
* Return a BigNumber whose value is the value of this BigNumber exponentiated by n.
*
Expand Down
28 changes: 28 additions & 0 deletions test/methods/euler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
if (typeof Test === 'undefined') require('../tester');

Test('euler', function () {
function t(digits, expected) {
BigNumber.set({DECIMAL_PLACES: digits})
Test.areEqual(String(expected), String(new BigNumber.euler()));
}

Test.areEqual(BigNumber.e, BigNumber.euler);

BigNumber.config({
DECIMAL_PLACES: 20,
ROUNDING_MODE: 4,
EXPONENTIAL_AT: [-7, 21],
RANGE: 1E9
});

t(1, '3')
t(2, '2.7')
t(10,'2.718281828')
t(20,'2.7182818284590452354')
t(30,'2.71828182845904523536028747135')
t(37, "2.718281828459045235360287471352662498")
t(130, "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413597")
t(306, "2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517")
// Performance drops significantly at around 1000+ digits
t(1000, "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011573834187930702154089149934884167509244761460668082264800168477411853742345442437107539077744992069551702761838606261331384583000752044933826560297606737113200709328709127443747047230696977209310141692836819025515108657463772111252389784425056953696770785449969967946864454905987931636889230098793127736178215424999229576351482208269895193668033182528869398496465105820939239829488793320362509443117301238197068416140397019837679320683282376464804295311802328782509819455815301756717361332069811250996181881593041690351598888519345807273866738589422879228499892086805825749279610484198444363463244968487560233624827041978623209002160990235304369941849146314093431738143640546253152096183690888707016768396424378140592714563549061303107208510383750510115747704171898610687396965521267154688957035035")
});
51 changes: 51 additions & 0 deletions test/methods/logarithm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
if (typeof Test === 'undefined') require('../tester');

Test('logarithm', function () {
var n = 'null',
N = 'NaN',
I = 'Infinity';

function t(number, base, expected) {
Test.areEqual(String(expected), String(new BigNumber(number).log(base)));
}

Test.areEqual(BigNumber.prototype.log, BigNumber.prototype.logBase);

BigNumber.config({
DECIMAL_PLACES: 20,
ROUNDING_MODE: 4,
EXPONENTIAL_AT: [-7, 21],
RANGE: 1E9
});

// Edge case tests
t(I, I, N)
t(I, '-' + I, N)
t(0, I, N)
t(-0, I, N)
t(I, -0, N)
t(I, 0, N)
t(0, 0, N)
t(1, 0, N)
t(0, 1, N)
t(-1, 1, N)
t(1, -1, N)
t(I, 1, N)

// Value Tests
t(10, 2, '3.3219280948873623479')
t('10.332323234', '0.434345', '-2.8003741336205112144')
t(100, 2, '6.6438561897747246957')
t(1000, 2, '9.9657842846620870436')
t(10, 4, '1.6609640474436811739')
t('3.3454545656', '.32764737467657', '-1.0822583142747469393')
t('9', '3', '2')
t('4', '2', '2')
t('95367431640625', '5', '20')
t(new BigNumber(1).div('95367431640625'), '5', '-20')
t(new BigNumber(1).div('95367431640625'), '.2', '20')
t('.47892384765743865096789478675847699', '.83426478236576437584768549685496', '4.0628897799653394675')
t('47892384765743865096789478675847699', '83426478236576437584768549685496', '1.0864301333053086708')
t('1', new BigNumber.euler(), '0')
t(new BigNumber.euler(), new BigNumber.euler(), '1')
});
2 changes: 2 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ console.log('\n Testing bignumber.js\n');
'dividedBy',
'dividedToIntegerBy',
'decimalPlaces',
'euler',
'exponentiatedBy',
'integerValue',
'isBigNumber',
'logarithm',
'minmax',
'minus',
'modulo',
Expand Down