The math.js library's many features include comprehensive support for units, enabling us to carry out calculations and conversions of measurements including length, mass, volume and many more.
The library has its own comprehensive documentation so in this article I will just give a brief introduction the unit functionality before providing a few examples of more obscure or specialized usage.
The project consists of a JavaScript file, an HTML file and a CSS file which you can download as a zip from the link below, or clone/download the Github repository if you prefer. The zip file and repository also include the minimized math.js library.
Source Code Links
The JavaScript file has nine functions to demonstrate various aspects of math.js units so I will look at each individually, but firstly let's look at window.onload and a utility function to output text.
mathjsunits.js part 1 - intro
window.onload = function() { output("Math.js Units<br>=============<br><br>"); createAndConvert(); // arithmetic(); // valueProperty(); // compatibility(); // astronomical(); // gravity(); // power(); // gallons(); // bitsAndBytes(); } function output(text) { const body = document.body; const p = document.createElement("p"); p.innerHTML = text; body.appendChild(p); }
In onload are a number of calls to various functions which can be uncommented/commented to run one at a time.
The output function adds text to the page as a <p> so we can use the page a bit like a console. Incidentally, I have formatted the page with a green monospace font on a black background just to enhance the illusion.
Basic Usage
The createAndConvert function demonstrates the basic usage of math.js units. Firstly we create a few unit objects and output them directly which will show the value and unit, for example 50 cm.
Next they are converted to other units, this being the core functionality of the whole unit system. Next the numeric values are retrieved using toNumber, and finally the underlying values in the appropriate SI units are accessed using toSI which this is not necessarily the same as the toNumber value.
mathjsunits.js part 2 - createAndConvert
function createAndConvert() { // create a few math.unit objects // we need to specify the value and the unit const centimetres = math.unit(50, 'cm'); const metres = math.unit(0.5, 'm'); const grams = math.unit(500, 'gram'); // this will display the value and unit, eg "50 cm" output("Units as created<br>----------------"); output(`centimetres: ${centimetres}`); output(`metres: ${metres}`); output(`grams: ${grams}`); output("<br>"); // units can be converted both within the same system, // eg centimetres to metres, or different systems // eg metres to feet output("Converted to other units<br>------------------------"); output(`centimetres to metres: ${centimetres.to('meter')}`); output(`metres to feet: ${metres.to('foot')}`); output(`grams to kilograms: ${grams.to('kilogram')}`); output("<br>"); // toNumber gets the numeric value output("toNumber<br>--------"); output(`centimetres.toNumber(): ${centimetres.toNumber()}`); output(`metres.toNumber(): ${metres.toNumber()}`); output(`grams.toNumber(): ${grams.toNumber()}`); output("<br>"); // all units are stored internally in SI Units // which can be accessed using the toSI method output("toSI<br>----"); output(`centimetres.toSI(): ${centimetres.toSI()}`); output(`metres.toSI(): ${metres.toSI()}`); output(`grams.toSI(): ${grams.toSI()}`); }
This is the output of the function which you can run yourself just by opening mathjsunits.htm in your browser.
Math.js Units ============= Units as created ---------------- centimetres: 50 cm metres: 0.5 m grams: 500 gram Converted to other units ------------------------ centimetres to metres: 0.5 meter metres to feet: 1.6404199475065615 foot grams to kilograms: 0.5 kilogram toNumber -------- centimetres.toNumber(): 50 metres.toNumber(): 0.5 grams.toNumber(): 500 toSI ---- centimetres.toSI(): 0.5 m metres.toSI(): 0.5 m grams.toSI(): 0.5 kg
Arithmetic
Now let's do some arithmetic. These are just a few examples, and there is a full list of arithmetic methods in the documentation.
Arithmetic can be carried out between different types of unit as long as they measure the same dimension such as length, mass etc. We can therefore mix and match centimetres and metres for example as I have done here to demonstrate equals, add, multiply and pow.
mathjsunits.js part 3 - arithmetic
function arithmetic() { const centimetres = math.unit(25, 'cm'); const metres = math.unit(2.5, 'm'); // check equality output(`centimetres.equals(metres): ${centimetres.equals(metres)}`); output("<br>"); // addition // units are normalized so we get the correct result // the result is in the unit of the first argument, // in this case centimetres const length = math.add(centimetres, metres); output(`math.add(centimetres, metres): ${length}`); output("<br>"); // multiplication & exponentiation // units are set as appropriate, // in this case m^2 and m^3 respectively const area = math.multiply(metres, metres); output(`math.multiply(metres, metres): ${area.to('m^2')}`); output(`math.pow(metres, 3): ${math.pow(metres, 3)}`); }
If you uncomment arithmetic in onload and refresh the page you'll see this.
Math.js Units ============= centimetres.equals(metres): false math.add(centimetres, metres): 275 cm math.multiply(metres, metres): 6.25 m^2 math.pow(metres, 3): 15.625 m^3
Beware of the Value Property
The unit object has a property called value which you can set. However, it is not advisable to do so at it holds the underlying SI value even for objects created with a different type so could lead to tricky bugs.
As a demonstration of this I have created a unit of 50 centimetres and then set value to 150. It would be easy to believe the value is changed to 150cm but it is actually now 150 metres which is unlikely to be the intended outcome.
There is actually no reason to use value at all. Leave well alone!
mathjsunits.js part 4 - valueProperty
function valueProperty() { const centimetres = math.unit(50, 'cm'); output(`centimetres: ${centimetres}`); output(`centimetres.value: ${centimetres.value}`); // setting value will give a result in metres // despite the unit being cm centimetres.value = 150; output(`centimetres.value = 150: ${centimetres}`); // metres output("VALUE IS NOW 150 m, not cm WHICH IS PROBABLY UNINTENDED"); }
If you uncomment valueProperty and refresh the page this is the result.
Math.js Units ============= centimetres: 50 cm centimetres.value: 0.5 centimetres.value = 150: 150 m VALUE IS NOW 150 m, not cm WHICH IS PROBABLY UNINTENDED
Unit Compatibility
The next function demonstrates what happens if you try to mix up different dimensions, and firstly I have tried to add a distance and a temperature. Not surprisingly we get an error.
Some mixing is meaningful however, such as dividing distance by time to calculate an average speed as in the second example.
mathjsunits.js part 5 - compatibility
function compatibility() { // different types const distance = math.unit(120, 'km'); const temperature = math.unit(20, 'degC'); const time = math.unit(60, 'minute'); // here we are trying to add km and degC which // is meaningless and will throw an exception try { output(`math.add(distance, temperature): ${math.add(distance, temperature)}`); } catch (e) { output(`math.add(distance, temperature): ${e}`); } // dividing distance by time will give a result as km / minute output(`math.divide(distance, time): ${math.divide(distance, time)}`); }
This is the output.
Math.js Units ============= math.add(distance, temperature): Error: Units do not match math.divide(distance, time): 2 km / minute
Astronomical Distances
The rest of the functions are slightly more advanced, and the first of these creates three new distance units based on the existing kilometre. These are lightyear (the distance light travels in a year), parsec (which is difficult to explain briefly but you might like to read the Wikipedia article on the topic) and Astronomical Unit or AU, the average distance from Earth to the Sun.
As you can see these are created using createUnit with the name and conversion factor as arguments.
mathjsunits.js part 6 - astronomical
function astronomical() { // create length units for astronomy math.createUnit('lightyear', '9460730777119.56 km'); math.createUnit('parsec', '30856775714409.19 km'); math.createUnit('AU', '149597870.7 km'); // Proxima Centauri is the nearest star to the Sun const EarthToProximaCentauri = math.unit(4.2465, 'lightyear'); output(`EarthToProximaCentauri: ${EarthToProximaCentauri}`); output(`EarthToProximaCentauri.to('km'): ${EarthToProximaCentauri.to('km')}`); output(`EarthToProximaCentauri.to('parsec'): ${EarthToProximaCentauri.to('parsec')}`); output("<br>"); const EarthToSun = math.unit(1.0, 'AU'); output(`EarthToSun: ${EarthToSun}`); output(`EarthToSun.to('miles'): ${EarthToSun.to('miles')}`); output(`EarthToSun.to('miles').toNumber(): ${EarthToSun.to('miles').toNumber()}`); }
I have used the lightyear unit to create an object representing the distance from Earth to the star Proxima Centauri which is then output in lightyears, kilometres and parsecs.
I have then used astronomical units to display the distance from Earth to the Sun in miles. Note that math.js shows this in scientific notation so I have also used toNumber to show the full value.
Math.js Units ============= EarthToProximaCentauri: 4.2465 lightyear EarthToProximaCentauri.to('km'): 4.017499324503822e+13 km EarthToProximaCentauri.to('parsec'): 1.3019828648615965 parsec EarthToSun: 1 AU EarthToSun.to('miles'): 9.295580727302553e+7 miles EarthToSun.to('miles').toNumber(): 92955807.27302553
Gravity
Next I'll use math.js units to implement Newton's Law of Universal Gravitation, specifically to calculate how much a person weighing 75kg on Earth would weigh on Mars. This is the formula.
Firstly we need three unit objects: the masses of the person and planet, and the radius of the planet. (The size of the person is so small relative to the planet it can be ignored. If we were, for example, applying the formula to a planet and a moon we'd need the distance between their centres of gravity.)
The actual formula is implemented in four parts to prevent it flying off the edge of the screen, across the room and out of the window.
The Newton is a measure of force which isn't really the same as weight, but we can calculate the Earth weight equivalent as I have done here. Finally we just output the various values.
mathjsunits.js part 7 - gravity
function gravity() { // calculate the weight of a person on massMars // the values needed as math.unit objects const massPerson = math.unit(75, 'kg'); const massMars = math.unit(6.4171e23, 'kg'); const radiusMars = math.unit(3389500, 'm'); // implement formula in 4 stages using math.js methods const numerator = math.multiply(massPerson, massMars); const denominator = math.pow(radiusMars, 2); const quotient = math.divide(numerator, denominator); const newtons = math.multiply(math.gravitationConstant, quotient); // convert Newtons to kg const weightOnMars = math.unit(math.divide(newtons.toNumber(), 9.80665), 'kg'); output(`massPerson: ${massPerson}`); output(`massMars: ${massMars.to('kg')}`); output(`radiusMars: ${radiusMars.to('km')}`); output(`Newtons: ${newtons}`); output(`weightOnMars: ${weightOnMars.to('kg')}`); }
As you can see the hypothetical 75kg person would weigh about 38% of this on Mars.
Math.js Units ============= massPerson: 75 kg massMars: 6.4171e+23 kg radiusMars: 3389.5 km Newtons: 279.5982943124534 N weightOnMars: 28.51109138313832 kg
Horsepower
The math.js library provides an ambiguous unit called hp. Although not stated in the documentation this is mechanical horsepower or about 746 Watts. In Europe and elsewhere metric horsepower is used, usually abbreviated to PS (pferd starke which is German for horsepower) and about 735 Watts. If you just want a rough idea of the power of a car the difference isn't significant but it's worth remembering that there are a number of different types of horsepower.
I have created a new unit called ps to give it a distinctively different name from hp, with the corresponding conversion factor to/from Watts.
mathjsunits.js part 8 - power
function power() { // demonstrate creating custom units // this is metric horsepower, most commonly denoted by PS // for pferd starke, German for horsepower math.createUnit('ps', '735.49875 watt'); // The math.js hp is mechanical horsepower or approx 746 Watts const power = math.unit(100, 'hp'); output(`power: ${power}`); output(`power.to('kilowatt'): ${power.to('kilowatt')}`); output(`power.to('ps'): ${power.to('ps')}`); }
I have then created a 100hp unit object and output the kilowatt and PS equivalents.
Math.js Units ============= power: 100 hp power.to('kilowatt'): 74.56998715386 kilowatt power.to('ps'): 101.38696653646251 ps
Gallons
A larger and more significant discrepancy is that between US and Imperial gallons which are 3.785412 and 4.54609 litres respectively. The math.js implementation is US gallons although this is not stated in the documentation. I have created a new unit for Imperial gallons although it would also be possible to redefine gallons with the Imperial conversion factor. The code to do this is shown commented out and I think the better option is to create a new unit.
mathjsunits.js part 9 - gallons
function gallons() { // create imperial gallon unit math.createUnit('impgal', '4.54609 liter'); // could do this, but probably not advisable! // math.createUnit('gallon', '4.54609 liter', {override: true}) const usgallon = math.unit(1, 'gallon'); output(`usgallon: ${usgallon}`); output(`usgallon.to('liter'): ${usgallon.to('liter')}`); output("<br>"); const impgallon = math.unit(1, 'impgal'); output(`impgallon: ${impgallon}`); output(`impgallon.to('liter'): ${impgallon.to('liter')}`); }
As a demo I have created 1 US gallon and 1 Imperial gallon and shown the litre equivalents.
Math.js Units ============= usgallon: 1 gallon usgallon.to('liter'): 3.785412 liter impgallon: 1 impgal impgallon.to('liter'): 4.54609 liter
Bits and Bytes
Finally let's look at bits and bytes, and in particular the confusing area of kilo, mega and so on.
For most units the prefixes kilo, mega, giga and tera refer to powers of 10, as shown in this table.
Prefix | Power | Multiplier |
---|---|---|
kilo | 103 | 1,000 |
mega | 106 | 1,000,000 |
giga | 109 | 1,000,000,000 |
tera | 1012 | 1,000,000,000,000 |
peta | 1015 | 1,000,000,000,000,000 |
exa | 1018 | 1,000,000,000,000,000,000 |
However, for memory (but not backing stores or transfer rates) the various commonly used prefixes actually mean powers of 2. For example 1 kilobyte of memory is 210 which comes in at 1024, not 1000. There is actually a separate set of prefixes specifically for use with bytes and powers of 2 but they are little known and not widely used.
The table below illustrates how they work. The decimal prefixes are of course almost universally used for bytes even though they are technically incorrect.
Decimal Prefix | Binary Prefix | Power | Multiplier |
---|---|---|---|
kilo | kibi | 210 | 1,024 |
mega | mebi | 220 | 1,048,576 |
giga | gibi | 230 | 1,073,741,824 |
tera | tebi | 240 | 1,099,511,627,776 |
peta | pebi | 250 | 1,125,899,906,842,624 |
exa | exbi* | 260 | 1,152,921,504,606,846,976 |
* math.js calls this exi which is incorrect.
math.js supports both the decimal and binary prefixes, and uses powers of 10 for bytes as well as other units so for example 1 kilobyte in math.js is 1000 bytes, not 1024 bytes.
I have put together a short piece of code to show bits, bytes and the decimal and binary prefixes in use.
The first const is 64 bits and the second is this value converted to bytes. The last const is 1024 bytes and we'll see what math.js does with this.
mathjsunits.js part 10 - bitsAndBytes
function bitsAndBytes() { const bits = math.unit(64, 'bits'); const bytes = bits.to('bytes'); const kilobytes = math.unit(1024, 'bytes'); output(`bits: ${bits}`); output(`bytes: ${bytes}`); output(`kilobytes: ${kilobytes}`); output(`kilobytes to kibibytes: ${kilobytes.to('kibibytes')}`); }
Outputting bits and bytes gives 64 and 8 respectively. No surprises there.
If we output the const called kilobytes (which remember is 1024 bytes) we actually get 1.024 kilobytes, demonstrating what I mentioned about math.js regarding kilobytes as 1000, not 1024.
If we then convert the 1024 byte value to kibibytes we get 1, proving that 1 kibibyte is 210 and not 103.
Math.js Units ============= bits: 64 bits bytes: 8 bytes kilobytes: 1.024 kilobytes kilobytes to kibibytes: 1 kibibytes
Conclusion
There is much more to math.js units than I have covered here and I hope you can find time to at least skim the official documentation. I also hope you'll consider using units next time you need to handle any of the units it provides, especially if you need to carry out any calculations or conversions.
Math.js is a firm favourite of mine and I have a few other articles in the pipeline exploring more of its functionality.