Exploring Units in Math.js

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

ZIP File
GitHub

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.

PrefixPowerMultiplier
kilo1031,000
mega1061,000,000
giga1091,000,000,000
tera10121,000,000,000,000
peta10151,000,000,000,000,000
exa10181,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 PrefixBinary PrefixPowerMultiplier
kilokibi2101,024
megamebi2201,048,576
gigagibi2301,073,741,824
teratebi2401,099,511,627,776
petapebi2501,125,899,906,842,624
exaexbi*2601,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.