Merge pull request #434 from ericman314/units-extra-fxns

Added support for units to abs, cube, sign, sqrt, square
This commit is contained in:
Jos de Jong 2015-08-21 09:03:36 +02:00
commit df9270a67f
12 changed files with 89 additions and 32 deletions

View File

@ -88,9 +88,8 @@ var incorrect = math.unit('8.314 m^3 Pa / mol K'); // Unit 8.314 (m^3 P
## Calculations
Basic operations `add`, `subtract`, `multiply`, `divide`, and `pow` can be performed
on units. Trigonometric functions like `sin` support units with an angle as
argument.
The operations that support units are `add`, `subtract`, `multiply`, `divide`, `pow`, `abs`, `sqrt`, `square`, `cube`, and `sign`.
Trigonometric functions like `cos` are also supported when the argument is an angle.
```js
var a = math.unit(45, 'cm'); // Unit 450 mm
@ -119,6 +118,22 @@ var q = math.eval('1 C'); // 1 C
var F = math.multiply(q, math.cross(v, B)); // [0 N, 0 N, -1 N]
```
All arithmetic operators act on the value of the unit as it is represented in SI units.
This may lead to surprising behavior when working with temperature scales like `celsius` (or `degC`) and `fahrenheit` (or `degF`).
In general you should avoid calculations using `celsius` and `fahrenheit`. Rather, use `kelvin` (or `K`) and `rankine` (or `R`) instead.
This example highlights some problems when using `celsius` and `fahrenheit` in calculations:
```js
var T_14F = math.unit('14 degF'); // Unit 14 degF (263.15 K)
var T_28F = math.multiply(T1, 2); // Unit 487.67 degF (526.3 K), not 28 degF
var Tnegative = math.unit(-13, 'degF'); // Unit -13 degF (248.15 K)
var Tpositive = math.abs(T1); // Unit -13 degF (248.15 K), not 13 degF
var Trate1 = math.eval('5 (degC/hour)'); // Unit 5 degC/hour
var Trate2 = math.eval('(5 degC)/hour'); // Unit 278.15 degC/hour
```
The expression parser supports units too. This is described in the section about
units on the page [Syntax](../expressions/syntax.md#units).

View File

@ -61,7 +61,7 @@ console.log();
console.log('compute speed of fluid flowing out of hole in a container');
var g = math.unit('9.81 m / s^2');
var h = math.unit('1 m');
var v = math.pow(math.multiply(2, math.multiply(g, h)), 0.5);
var v = math.pow(math.multiply(2, math.multiply(g, h)), 0.5); // Can also use math.sqrt
console.log('g = ' + format(g));
console.log('h = ' + format(h));
console.log('v = (2 g h) ^ 0.5 = ' + format(v)); // 4.429... m / s

View File

@ -22,9 +22,9 @@ function factory (type, config, load, typed) {
*
* sign
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x
* A number or matrix for which to get the absolute value
* @return {number | BigNumber | Fraction | Complex | Array | Matrix}
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit}
* Absolute value of `x`
*/
var abs = typed('abs', {
@ -60,6 +60,14 @@ function factory (type, config, load, typed) {
'Array | Matrix': function (x) {
// deep map collection, skip zeros since abs(0) = 0
return deepMap(x, abs, true);
},
'Unit': function(x) {
// This gives correct, but unexpected, results for units with an offset.
// For example, abs(-283.15 degC) = -263.15 degC !!!
var ret = x.clone();
ret.value = Math.abs(ret.value);
return ret;
}
});

View File

@ -26,8 +26,8 @@ function factory (type, config, load, typed) {
*
* multiply, square, pow
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x Number for which to calculate the cube
* @return {number | BigNumber | Fraction | Complex | Array | Matrix} Cube of x
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x Number for which to calculate the cube
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} Cube of x
*/
var cube = typed('cube', {
'number': function (x) {
@ -49,6 +49,10 @@ function factory (type, config, load, typed) {
'Array | Matrix': function (x) {
// deep map collection, skip zeros since cube(0) = 0
return deepMap(x, cube, true);
},
'Unit': function(x) {
return x.pow(3);
}
});

View File

@ -29,9 +29,9 @@ function factory (type, config, load, typed) {
*
* abs
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x
* The number for which to determine the sign
* @return {number | BigNumber | Fraction | Complex | Array | Matrix}e
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit}e
* The sign of `x`
*/
var sign = typed('sign', {
@ -53,6 +53,10 @@ function factory (type, config, load, typed) {
'Array | Matrix': function (x) {
// deep map collection, skip zeros since sign(0) = 0
return deepMap(x, sign, true);
},
'Unit': function(x) {
return number.sign(x.value);
}
});

View File

@ -22,9 +22,9 @@ function factory (type, config, load, typed) {
*
* square, multiply
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* @param {number | BigNumber | Complex | Array | Matrix | Unit} x
* Value for which to calculate the square root.
* @return {number | BigNumber | Complex | Array | Matrix}
* @return {number | BigNumber | Complex | Array | Matrix | Unit}
* Returns the square root of `x`
*/
var sqrt = typed('sqrt', {
@ -45,7 +45,13 @@ function factory (type, config, load, typed) {
'Array | Matrix': function (x) {
// deep map collection, skip zeros since sqrt(0) = 0
return deepMap(x, sqrt, true);
},
'Unit': function (x) {
// Someday will work for complex units when they are implemented
return x.pow(0.5);
}
});
/**

View File

@ -24,9 +24,9 @@ function factory (type, config, load, typed) {
*
* multiply, cube, sqrt, pow
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x
* Number for which to calculate the square
* @return {number | BigNumber | Fraction | Complex | Array | Matrix}
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit}
* Squared value
*/
var square = typed('square', {
@ -52,6 +52,10 @@ function factory (type, config, load, typed) {
'Array | Matrix': function (x) {
// deep map collection, skip zeros since square(0) = 0
return deepMap(x, square, true);
},
'Unit': function(x) {
return x.pow(2);
}
});

View File

@ -69,10 +69,13 @@ describe('abs', function () {
assert.deepEqual(a1.valueOf(), [2,1,0,1,2])
});
it('should throw an error with a measurment unit', function () {
assert.throws(function () {
math.abs(math.unit(5, 'km'));
});
it('should return the absolute value of a unit', function () {
var u = math.abs(math.unit('5 m'));
assert.equal(u.toString(), '5 m');
u = math.abs(math.unit('-5 m'));
assert.equal(u.toString(), '5 m');
u = math.abs(math.unit('-283.15 degC'));
assert.equal(u.toString(), '-263.15 degC');
});
it('should throw an error in case of invalid number of arguments', function() {
@ -86,8 +89,8 @@ describe('abs', function () {
});
it('should LaTeX abs', function () {
var expression = math.parse('abs(-1)');
assert.equal(expression.toTex(),'\\left|-1\\right|');
var expression = math.parse('abs(-1)');
assert.equal(expression.toTex(),'\\left|-1\\right|');
});
});

View File

@ -43,12 +43,14 @@ describe('cube', function() {
assert.deepEqual(cube(math.complex('2')), math.complex('8'));
});
it('should throw an error with strings', function() {
assert.throws(function () {cube('text')});
it('should return the cube of a unit', function() {
assert.equal(cube(math.unit('4 cm')).toString(), '64 cm^3');
assert.equal(cube(math.unit('-2 cm')).toString(), '-8 cm^3');
assert.equal(cube(math.unit('0 cm')).toString(), '0 cm^3');
});
it('should throw an error with units', function() {
assert.throws(function () {cube(unit('5cm'))});
it('should throw an error with strings', function() {
assert.throws(function () {cube('text')});
});
it('should throw an error if there\'s wrong number of args', function() {

View File

@ -39,8 +39,13 @@ describe('sign', function() {
approx.deepEqual(math.sign(math.complex(2,-3)), math.complex(0.554700196225229, -0.832050294337844));
});
it('should throw an error when used with a unit', function() {
assert.throws(function () { math.sign(math.unit('5cm')); });
it('should calculate the sign of a unit', function() {
assert.equal(math.sign(math.unit('5 cm')), 1);
assert.equal(math.sign(math.unit('-5 kg')), -1);
assert.equal(math.sign(math.unit('0 mol/s')), 0);
assert.equal(math.sign(math.unit('-283.15 degC')), -1);
assert.equal(math.sign(math.unit('-273.15 degC')), 0);
assert.equal(math.sign(math.unit('-263.15 degC')), 1);
});
it('should throw an error when used with a string', function() {

View File

@ -64,10 +64,14 @@ describe('sqrt', function() {
assert.deepEqual(sqrt(math.complex(1e10, 1e-10)), math.complex(1e5, 5e-16));
});
it('should throw an error when used with a unit', function() {
assert.throws(function () {
sqrt(math.unit(5, 'km'));
});
it('should return the square root of a unit', function() {
assert.equal(sqrt(math.unit('25 m^2/s^2')).toString(), '5 m / s');
assert.equal(sqrt(math.unit('4 kg')).toString(), '2 kg^0.5');
});
it('should return NaN when computing the square root of a negative unit', function() {
// Update this when support for complex units is added
assert.equal(sqrt(math.unit('-25 m^2/s^2')).toString(), 'NaN m / s');
});
it('should throw an error when used with a string', function() {

View File

@ -48,8 +48,10 @@ describe('square', function() {
assert.deepEqual(square(math.complex('2')), math.complex('4'));
});
it('should throw an error when used with a unit', function() {
assert.throws(function () {square(unit('5cm'))});
it('should return the square of a unit', function() {
assert.equal(square(math.unit('4 cm')).toString(), '16 cm^2');
assert.equal(square(math.unit('-2 cm')).toString(), '4 cm^2');
assert.equal(square(math.unit('0 cm')).toString(), '0 cm^2');
});
it('should throw an error when used with a string', function() {