mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-25 15:07:57 +00:00
Merge pull request #1060 from dakotablair/develop
Fixed #851: More consistent behavior of sqrt, nthRoot, and pow
This commit is contained in:
commit
138bebd655
@ -1,5 +1,12 @@
|
||||
# History
|
||||
|
||||
|
||||
## 2017-03-17, version 4.0.1
|
||||
|
||||
- Fixed #1062: mathjs not working on ES5 browsers like IE11 and Safari 9.3.
|
||||
- Fixed #1061: `math.unit` not accepting input like `1/s`.
|
||||
|
||||
|
||||
## 2018-02-25, version 4.0.0
|
||||
|
||||
!!! BE CAREFUL: BREAKING CHANGES !!!
|
||||
|
||||
11043
dist/math.js
vendored
11043
dist/math.js
vendored
File diff suppressed because it is too large
Load Diff
8
dist/math.min.js
vendored
8
dist/math.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/math.min.map
vendored
2
dist/math.min.map
vendored
File diff suppressed because one or more lines are too long
@ -23,17 +23,23 @@ The following configuration options are available:
|
||||
determined by the option `matrix`. In case of mixed matrix
|
||||
inputs, a matrix will be returned always.
|
||||
|
||||
- `number`. The default type of numbers. This setting is used by functions
|
||||
like `eval` which cannot determine the correct type of output from the
|
||||
functions input. For most functions though, the type of output is determined
|
||||
from the the input: a number as input will return a number as output,
|
||||
- `number`. The type of numeric output for functions which cannot
|
||||
determine the numeric type from the inputs. For most functions though,
|
||||
the type of output is determined from the the input:
|
||||
a number as input will return a number as output,
|
||||
a BigNumber as input returns a BigNumber as output.
|
||||
|
||||
For example the functions `math.eval('2+3')`, `math.parse('2+3')`,
|
||||
`math.range('1:10')`, and `math.unit('5cm')` use the `number` configuration
|
||||
setting. But `math.sqrt(4)` will always return the number `2`
|
||||
regardless of the `number` configuration, because the input is a number.
|
||||
|
||||
Available values are: `'number'` (default), `'BigNumber'`, or `'Fraction'`.
|
||||
[BigNumbers](../datatypes/bignumbers.js) have higher precision than the default
|
||||
numbers of JavaScript, and [`Fractions`](../datatypes/fractions.js) store
|
||||
values in terms of a numerator and denominator.
|
||||
|
||||
- `precision`. The maximum number of significant digits for bigNumbers.
|
||||
- `precision`. The maximum number of significant digits for BigNumbers.
|
||||
This setting only applies to BigNumbers, not to numbers.
|
||||
Default value is `64`.
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ math.isPrime(0); // returns false
|
||||
math.isPrime(-0); // returns false
|
||||
math.isPrime(0.5); // returns false
|
||||
math.isPrime('2'); // returns true
|
||||
math.isPrime([2, 17, 100]'); // returns [true, true, false]
|
||||
math.isPrime([2, 17, 100]); // returns [true, true, false]
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -15,7 +15,8 @@ module.exports = {
|
||||
'sqrt(9)'
|
||||
],
|
||||
'seealso': [
|
||||
'sqrt',
|
||||
'pow'
|
||||
'nthRoots',
|
||||
'pow',
|
||||
'sqrt'
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
23
lib/expression/embeddedDocs/function/arithmetic/nthRoots.js
Normal file
23
lib/expression/embeddedDocs/function/arithmetic/nthRoots.js
Normal file
@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
'name': 'nthRoots',
|
||||
'category': 'Arithmetic',
|
||||
'syntax': [
|
||||
'nthRoots(A)',
|
||||
'nthRoots(A, root)'
|
||||
],
|
||||
'description': (''
|
||||
+ 'Calculate the nth roots of a value. '
|
||||
+ 'An nth root of a positive real number A, '
|
||||
+ 'is a positive real solution of the equation "x^root = A". '
|
||||
+ 'This function returns an array of complex values.'
|
||||
),
|
||||
'examples': [
|
||||
'nthRoots(1) == [ { re: 1, im: 0 }, { re: -1, im: 0 } ]',
|
||||
'nthRoots(1, 3) == [ { re: 1, im: 0 }, { re: -0.4999999999999998, im: 0.8660254037844387 }, { re: -0.5000000000000004, im: -0.8660254037844385 } ] ]'
|
||||
],
|
||||
'seealso': [
|
||||
'sqrt',
|
||||
'pow',
|
||||
'nthRoot'
|
||||
]
|
||||
};
|
||||
@ -12,5 +12,10 @@ module.exports = {
|
||||
'2*2*2',
|
||||
'1 + e ^ (pi * i)'
|
||||
],
|
||||
'seealso': [ 'multiply' ]
|
||||
'seealso': [
|
||||
'multiply',
|
||||
'nthRoot',
|
||||
'nthRoots',
|
||||
'sqrt'
|
||||
]
|
||||
};
|
||||
|
||||
@ -13,6 +13,9 @@ module.exports = {
|
||||
],
|
||||
'seealso': [
|
||||
'square',
|
||||
'multiply'
|
||||
'multiply',
|
||||
'nthRoot',
|
||||
'nthRoots',
|
||||
'pow'
|
||||
]
|
||||
};
|
||||
|
||||
@ -130,6 +130,7 @@ function factory (construction, config, load, typed) {
|
||||
docs.multiply = require('./function/arithmetic/multiply');
|
||||
docs.norm = require('./function/arithmetic/norm');
|
||||
docs.nthRoot = require('./function/arithmetic/nthRoot');
|
||||
docs.nthRoots = require('./function/arithmetic/nthRoots');
|
||||
docs.pow = require('./function/arithmetic/pow');
|
||||
docs.round = require('./function/arithmetic/round');
|
||||
docs.sign = require('./function/arithmetic/sign');
|
||||
|
||||
@ -21,6 +21,7 @@ module.exports = [
|
||||
require('./multiply'),
|
||||
require('./norm'),
|
||||
require('./nthRoot'),
|
||||
require('./nthRoots'),
|
||||
require('./pow'),
|
||||
require('./round'),
|
||||
require('./sign'),
|
||||
|
||||
@ -40,6 +40,10 @@ function factory (type, config, load, typed) {
|
||||
* @param {number | BigNumber} [root=2] The root.
|
||||
* @return {number | Complex | Array | Matrix} Returns the nth root of `a`
|
||||
*/
|
||||
var complex_err = (''
|
||||
+ 'Complex number not supported in function nthRoot. '
|
||||
+ 'Use nthRoots instead.'
|
||||
);
|
||||
var nthRoot = typed('nthRoot', {
|
||||
|
||||
'number': function (x) {
|
||||
@ -51,9 +55,11 @@ function factory (type, config, load, typed) {
|
||||
return _bigNthRoot(x, new type.BigNumber(2));
|
||||
},
|
||||
'Complex' : function(x) {
|
||||
return _nthComplexRoot(x, 2);
|
||||
throw new Error(complex_err);
|
||||
},
|
||||
'Complex, number' : _nthComplexRoot,
|
||||
'Complex, number' : function(x, y) {
|
||||
throw new Error(complex_err);
|
||||
},
|
||||
'BigNumber, BigNumber': _bigNthRoot,
|
||||
|
||||
'Array | Matrix': function (x) {
|
||||
@ -245,25 +251,5 @@ function _nthRoot(a, root) {
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the nth root of a Complex Number a using De Moviers Theorem.
|
||||
* @param {Complex} a
|
||||
* @param {number} root
|
||||
* @return {Array} array or n Complex Roots in Polar Form.
|
||||
*/
|
||||
function _nthComplexRoot(a, root) {
|
||||
if (root < 0) throw new Error('Root must be greater than zero');
|
||||
if (root === 0) throw new Error('Root must be non-zero');
|
||||
if (root % 1 !== 0) throw new Error('Root must be an integer');
|
||||
var arg = a.arg();
|
||||
var abs = a.abs();
|
||||
var roots = [];
|
||||
var r = Math.pow(abs, 1/root);
|
||||
for(var k = 0; k < root; k++) {
|
||||
roots.push({r: r, phi: (arg + 2 * Math.PI * k)/root});
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
exports.name = 'nthRoot';
|
||||
exports.factory = factory;
|
||||
|
||||
76
lib/function/arithmetic/nthRoots.js
Normal file
76
lib/function/arithmetic/nthRoots.js
Normal file
@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
|
||||
var Complex = require('../../type/complex/Complex');
|
||||
var typed = require('../../core/typed');
|
||||
var complex = Complex.factory(
|
||||
'Complex', {}, '', typed, {on: function(x, y){}}
|
||||
);
|
||||
|
||||
function factory (type, config, load, typed) {
|
||||
var nthRoots = typed('nthRoots', {
|
||||
'Complex' : function(x) {
|
||||
return _nthComplexRoots(x, 2);
|
||||
},
|
||||
'Complex, number' : _nthComplexRoots,
|
||||
});
|
||||
nthRoots.toTex = {2: '\\{y : $y^{args[1]} = {${args[0]}}\\}'};
|
||||
return nthRoots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each function here returns a real multiple of i as a Complex value.
|
||||
* @param {number} val
|
||||
* @return {Complex} val, i*val, -val or -i*val for index 0, 1, 2, 3
|
||||
*/
|
||||
// This is used to fix float artifacts for zero-valued components.
|
||||
var _calculateExactResult = [
|
||||
function realPos(val){return complex(val);},
|
||||
function imagPos(val){return complex(0, val);},
|
||||
function realNeg(val){return complex(-val);},
|
||||
function imagNeg(val){return complex(0, -val);}
|
||||
];
|
||||
|
||||
/**
|
||||
* Calculate the nth root of a Complex Number a using De Movire's Theorem.
|
||||
* @param {Complex} a
|
||||
* @param {number} root
|
||||
* @return {Array} array of n Complex Roots
|
||||
*/
|
||||
function _nthComplexRoots(a, root) {
|
||||
if (root < 0) throw new Error('Root must be greater than zero');
|
||||
if (root === 0) throw new Error('Root must be non-zero');
|
||||
if (root % 1 !== 0) throw new Error('Root must be an integer');
|
||||
if (a === 0 || a.abs() === 0) return [complex(0)];
|
||||
var aIsNumeric = typeof(a) === 'number';
|
||||
var offset;
|
||||
// determine the offset (argument of a)/(pi/2)
|
||||
if (aIsNumeric || a.re === 0 || a.im === 0) {
|
||||
if (aIsNumeric) {
|
||||
offset = 2*(+(a < 0)); // numeric value on the real axis
|
||||
} else if (a.im === 0) {
|
||||
offset = 2*(+(a.re < 0)); // complex value on the real axis
|
||||
} else {
|
||||
offset = 2*(+(a.im < 0)) + 1; // complex value on the imaginary axis
|
||||
}
|
||||
}
|
||||
var arg = a.arg();
|
||||
var abs = a.abs();
|
||||
var roots = [];
|
||||
var r = Math.pow(abs, 1/root);
|
||||
for(var k = 0; k < root; k++) {
|
||||
var halfPiFactor = (offset + 4*k)/root;
|
||||
/**
|
||||
* If (offset + 4*k)/root is an integral multiple of pi/2
|
||||
* then we can produce a more exact result.
|
||||
*/
|
||||
if (halfPiFactor === Math.round(halfPiFactor)) {
|
||||
roots.push(_calculateExactResult[halfPiFactor % 4](r));
|
||||
continue;
|
||||
}
|
||||
roots.push(complex({r: r, phi: (arg + 2 * Math.PI * k)/root}));
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
exports.name = 'nthRoots';
|
||||
exports.factory = factory;
|
||||
@ -247,8 +247,12 @@ function factory (type, config, load, typed, math) {
|
||||
var unit = new Unit();
|
||||
unit.units = [];
|
||||
|
||||
var powerMultiplierCurrent = 1;
|
||||
var expectingUnit = false;
|
||||
|
||||
// A unit should follow this pattern:
|
||||
// [number]unit[^number] [unit[^number]]...[/unit[^number] [unit[^number]]]
|
||||
// [number] ...[ [*/] unit[^number] ]
|
||||
// unit[^number] ... [ [*/] unit[^number] ]
|
||||
|
||||
// Rules:
|
||||
// number is any floating point number.
|
||||
@ -262,6 +266,7 @@ function factory (type, config, load, typed, math) {
|
||||
|
||||
next();
|
||||
skipWhitespace();
|
||||
|
||||
// Optional number at the start of the string
|
||||
var valueStr = parseNumber();
|
||||
var value = null;
|
||||
@ -275,12 +280,19 @@ function factory (type, config, load, typed, math) {
|
||||
else { // number
|
||||
value = parseFloat(valueStr);
|
||||
}
|
||||
}
|
||||
skipWhitespace(); // Whitespace is not required here
|
||||
|
||||
// Next, we read any number of unit[^number]
|
||||
var powerMultiplierCurrent = 1;
|
||||
var expectingUnit = false;
|
||||
skipWhitespace(); // Whitespace is not required here
|
||||
|
||||
// handle multiplication or division right after the value, like '1/s'
|
||||
if (parseCharacter('*')) {
|
||||
powerMultiplierCurrent = 1;
|
||||
expectingUnit = true;
|
||||
}
|
||||
else if (parseCharacter('/')) {
|
||||
powerMultiplierCurrent = -1;
|
||||
expectingUnit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Stack to keep track of powerMultipliers applied to each parentheses group
|
||||
var powerMultiplierStack = [];
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
module.exports = '4.0.0';
|
||||
module.exports = '4.0.1';
|
||||
// Note: This file is automatically generated when building math.js.
|
||||
// Changes made in this file will be overwritten.
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mathjs",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -5812,9 +5812,9 @@
|
||||
}
|
||||
},
|
||||
"typed-function": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.0.1.tgz",
|
||||
"integrity": "sha512-Ie5d+HS39FU+sKj5nzcSV9pucMOtHsomaZPaxX9CWnxeqcdBkGl0cGKx1xd5v+b1czUd1iVa/RMZbsN8wnfGPg=="
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.0.3.tgz",
|
||||
"integrity": "sha512-sVC/1pm70oELDFMdYtFXMFqyawenLoaDiAXA3QvOAwKF/WvFNTSJN23cY2lFNL8iP0kh3T0PPKewrboO8XUVGQ=="
|
||||
},
|
||||
"typedarray-pool": {
|
||||
"version": "1.1.0",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mathjs",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.1",
|
||||
"description": "Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types like numbers, big numbers, complex numbers, fractions, units, and matrices.",
|
||||
"author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)",
|
||||
"contributors": [
|
||||
@ -98,7 +98,7 @@
|
||||
"javascript-natural-sort": "0.7.1",
|
||||
"seed-random": "2.2.0",
|
||||
"tiny-emitter": "2.0.2",
|
||||
"typed-function": "1.0.1"
|
||||
"typed-function": "1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"benchmark": "2.1.4",
|
||||
|
||||
@ -110,21 +110,8 @@ describe('nthRoot', function() {
|
||||
assert.deepEqual(nthRoot(big(Infinity), big(-3)), big(0));
|
||||
});
|
||||
|
||||
it('should return an array of Complex Roots in Polar form', function() {
|
||||
var roots = nthRoot(complex("-1"), 6);
|
||||
var roots1 = [
|
||||
{r: 1, phi: Math.PI/6},
|
||||
{r: 1, phi: Math.PI/2},
|
||||
{r: 1, phi: (5 * Math.PI)/6},
|
||||
{r: 1, phi: (7 * Math.PI)/6},
|
||||
{r: 1, phi: (9 * Math.PI)/6},
|
||||
{r: 1, phi: (11 * Math.PI)/6}
|
||||
];
|
||||
|
||||
roots.forEach(function (value, index, array) {
|
||||
assert.equal(value.r, roots1[index].r);
|
||||
assert.equal(value.phi, roots1[index].phi);
|
||||
});
|
||||
it('should throw an error when used with a complex number', function() {
|
||||
assert.throws(function () {nthRoot(complex("-8"), 3);});
|
||||
});
|
||||
|
||||
it('should throw an error when used with a complex number and root is less than 0', function() {
|
||||
|
||||
74
test/function/arithmetic/nthRoots.test.js
Normal file
74
test/function/arithmetic/nthRoots.test.js
Normal file
@ -0,0 +1,74 @@
|
||||
// test nthRoots
|
||||
var assert = require('assert');
|
||||
var math = require('../../../index');
|
||||
var complex = math.complex;
|
||||
var nthRoots = math.nthRoots;
|
||||
|
||||
describe('nthRoots', function() {
|
||||
it('should return an array of Complex roots', function() {
|
||||
var roots = nthRoots(complex("-1"), 6);
|
||||
var roots1 = [
|
||||
complex({r: 1, phi: Math.PI/6}),
|
||||
complex(0, 1),
|
||||
complex({r: 1, phi: (5 * Math.PI)/6}),
|
||||
complex({r: 1, phi: (7 * Math.PI)/6}),
|
||||
complex(0, -1),
|
||||
complex({r: 1, phi: (11 * Math.PI)/6})
|
||||
];
|
||||
|
||||
roots.forEach(function (value, index, array) {
|
||||
assert.deepEqual(value, roots1[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the correct answer for Complex values', function() {
|
||||
var roots = nthRoots(complex(3, 4), 2);
|
||||
var roots1 = [
|
||||
{ re: 2, im: 1 },
|
||||
{ re: -2.0000000000000004, im: -0.9999999999999999}
|
||||
];
|
||||
roots.forEach(function (value, index, array) {
|
||||
assert.deepEqual(value, roots1[index]);
|
||||
});
|
||||
});
|
||||
|
||||
var twos = [
|
||||
complex(2, 0),
|
||||
complex(0, 2),
|
||||
complex(-2, 0),
|
||||
complex(0, -2),
|
||||
];
|
||||
it('should return pure roots without artifacts', function() {
|
||||
var roots = nthRoots(complex("16"), 4);
|
||||
|
||||
roots.forEach(function (value, index, array) {
|
||||
assert.deepEqual(value, twos[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return roots for numeric arguments', function() {
|
||||
var roots = nthRoots(16, 4);
|
||||
|
||||
roots.forEach(function (value, index, array) {
|
||||
assert.deepEqual(value, twos[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return roots for string arguments', function() {
|
||||
var roots = nthRoots("16", 4);
|
||||
|
||||
roots.forEach(function (value, index, array) {
|
||||
assert.deepEqual(value, twos[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return zero exactly once', function() {
|
||||
var roots2 = nthRoots(0);
|
||||
var roots4 = nthRoots(0, 4);
|
||||
var roots8 = nthRoots(0, 8);
|
||||
assert.deepEqual(roots2, [complex(0)]);
|
||||
assert.deepEqual(roots4, [complex(0)]);
|
||||
assert.deepEqual(roots8, [complex(0)]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -28,12 +28,14 @@ describe('pow', function() {
|
||||
|
||||
it('should exponentiate a negative number to a non-integer power', function() {
|
||||
approx.deepEqual(pow(-2,1.5), complex(0, -2.82842712474619));
|
||||
approx.deepEqual(pow(-8, 1/3), complex(1, 1.732050807568877));
|
||||
});
|
||||
|
||||
it('should exponentiate a negative number to a non-integer power with predictable:true', function() {
|
||||
var res = mathPredictable.pow(-2,1.5);
|
||||
assert.equal(typeof res, 'number');
|
||||
assert(isNaN(res));
|
||||
assert.strictEqual(mathPredictable.pow(-8, 1/3), -2);
|
||||
});
|
||||
|
||||
it('should return a real-valued root if one exists with predictable:true', function() {
|
||||
@ -130,7 +132,7 @@ describe('pow', function() {
|
||||
assert.throws(function () {pow(null, 2);}, /TypeError: Unexpected type of argument/);
|
||||
});
|
||||
|
||||
it('should handle infitie exponents', function() {
|
||||
it('should handle infinite exponents', function() {
|
||||
var Ptbl = mathPredictable;
|
||||
|
||||
// TODO replace isNaN with complexInfinity when complex.js updates
|
||||
|
||||
@ -861,6 +861,21 @@ describe('Unit', function() {
|
||||
unit1 = Unit.parse('5exabytes');
|
||||
approx.equal(unit1.value, 4e19);
|
||||
assert.equal(unit1.units[0].unit.name, 'bytes');
|
||||
|
||||
unit1 = Unit.parse('1 / s');
|
||||
approx.equal(unit1.value, 1);
|
||||
assert.equal(unit1.units[0].unit.name, 's');
|
||||
assert.equal(unit1.units[0].power, -1);
|
||||
|
||||
unit1 = Unit.parse('1/s');
|
||||
approx.equal(unit1.value, 1);
|
||||
assert.equal(unit1.units[0].unit.name, 's');
|
||||
assert.equal(unit1.units[0].power, -1);
|
||||
|
||||
unit1 = Unit.parse('1 * s');
|
||||
approx.equal(unit1.value, 1);
|
||||
assert.equal(unit1.units[0].unit.name, 's');
|
||||
assert.equal(unit1.units[0].power, 1);
|
||||
});
|
||||
|
||||
it('should parse expressions with nested parentheses correctly', function() {
|
||||
@ -921,6 +936,7 @@ describe('Unit', function() {
|
||||
assert.throws(function () {Unit.parse('meter.')}, /Unexpected "\."/);
|
||||
assert.throws(function () {Unit.parse('meter/')}, /Trailing characters/);
|
||||
assert.throws(function () {Unit.parse('/meter')}, /Unexpected "\/"/);
|
||||
assert.throws(function () {Unit.parse('1 */ s')}, /Unexpected "\/"/);
|
||||
assert.throws(function () {Unit.parse('45 kg 34 m')}, /Unexpected "3"/);
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user