Skip to content

kreativekorp/multiconvert-data

Repository files navigation

MultiConvert Data

This repository contains source data for the MultiConvert app.

Building and Testing

To build and test the data files, you need Node.js and UglifyJS (npm install uglify-js -g). The build script build.sh will load and validate all data files, run all tests, and if no errors are encountered, produce minified data files.

$ ./build.sh
Compiling...
194 unit types defined
16 functions defined
1194 units defined
2659 units defined or included
3 includes defined
119 elements defined
11 solvers or calculators defined
0 errors in data
0 warnings in data
865 tests executed
865 tests passed
0 tests failed
0 errors total
0 warnings total
Wrote mcdbmain.js
Wrote mcdbmisc.js
Minifying...
-rw-rw-r-- 1 user group 189969 May  1 18:52 mcdbmain.js
-rw-rw-r-- 1 user group 165284 May  1 18:52 mcdbmain.min.js
-rw-rw-r-- 1 user group  30999 May  1 18:52 mcdbmisc.js
-rw-rw-r-- 1 user group  26071 May  1 18:52 mcdbmisc.min.js
$

The minified data files are currently mostly useless to most people. If you are using this data in your own application, it is recommended to use the JSON source files for your own purposes and use the build script just for validation and testing.

Unit Identifiers

Units in the MultiConvert database are identified by a lowercase letter followed by one or more digits. The following ranges are currently used:

range file usage
u0u49 base-si.json Base units and coherent derived units in the International System of Units (SI).
u50u99 base-nonsi.json Base units and coherent derived units not recognized by SI.
u100u199 derived-si.json Non-SI units accepted for use with SI.
u200u299 derived-length.json Non-SI units of length.
u300u399 derived-area.json Non-SI units of area.
u400u499 derived-volume.json Non-SI units of volume.
u500u599 derived-vel-acc.json Non-SI units of velocity and acceleration.
u600u699 derived-mass.json Non-SI units of mass.
u700u799 derived-force.json Non-SI units of force.
u800u899 derived-time.json Non-SI units of time.
u900u999 derived‑temperature.json Non-SI units of temperature.
u1000u1099 derived-angle.json Non-SI units of angular displacement.
u1100u1199 derived-power.json Non-SI units of power.
u1200u1299 derived-pressure.json Non-SI units of pressure.
u1300u1399 derived-frequency.json Non-SI units of frequency.
u1400u1499 derived-voltage.json Non-SI units of voltage.
u1500u1599 derived-current.json Non-SI units of current.
u1600u1699 derived-data.json Units of data or information (bits and bytes).
u1700u1799 derived-sheets.json Units of page or sheet count (quires, reams, and bales).
u1800u1899 derived-hardness.json Units of hardness of materials.
u1900u1999 derived-misc.json Units expressed with a single decimal number not included in other categories.
u2000u2099 dimensionless.json Dimensionless units.
n100n199 viscosity.json Units of kinematic viscosity.
n200n299 shoe-size.json Shoe sizes.
n1000n1001 clock-time.json Biel Mean Time (also known as Swatch Internet Time).
z0z2 frequency-color.json
frequency-pitch.json
guaca.json
Units not expressed with a single decimal number not included in other categories.
z100z199 numeral-system.json Numeral systems (binary, octal, hexadecimal, Roman numerals, et cetera).
z200z299 coordinate-system.json Coordinate systems (Cartesian, polar, spherical, et cetera).
z300z399 color-space.json Color spaces (RGB, HSV, YIQ, YUV, et cetera).
z1000z1999 clock-time.json Wall clock time in different time zones.
k0k5 capacitor-code.json
inductor-code.json
resistor-code.json
Color codes and EIA codes for electronic components.
c0c19999 Units of currency. These are not present in this repository but generated dynamically by MultiConvert from third-party data.
d0d99 dependent.json Units for which conversion requires the specification of an independent variable (such as air temperature for Mach number).
m0m999 medical.json Units of concentration of various medications.
p0p99 planck.json Planck units.

Identifiers starting with e, f, i, s, and t are not used for units as they are used for other objects.

Identifiers starting with n are generally used for units for which mathematically exact conversion is not possible. (For some reason Biel Mean Time has an n identifier even though it's an exact conversion.)

Unit Expressions

Units created through the use of SI or IEEE prefixes or multiplication and division of base units are not defined in data files but derived mathematically, and as such are identified not by a single identifier but by a unit expression. For example:

expression unit
u0_3 kilometers
u0_-3 millimeters
u0^2 square meters
u0/u2 meters per second
u10*u0 newton meters
u700*u0_-2 dyne centimeters
u51.10 kibibits (u51 × 210)
u1602.20 mebibytes (u1602 × 220)
u13/u0^2*u4 watts (u13) per square meter (u0^2) kelvin (u4)
u1103/u210^2*u101*u902 British thermal units (u1103) per square foot (u210^2) hour (u101) degree Rankine (u902)
u1/u0^0.5*u2^2 kilograms (u1) per square root meter (u0^0.5) square second (u2^2)

As shown above, unit expressions can get arbitrarily complex.

Division in unit expressions has lower precedence than multiplication, so u0*u1/u2*u3 is equivalent to (u0*u1)/(u2*u3), not ((u0*u1)/u2)*u3.

Looking Up Unit Identifiers

If you need to look up an identifier, you can use the included mcvt.js utility program. You can look up by identifier, symbol, or name.

$ ./mcvt.js u0

d    id    type    sym    name      dimension
-    --    ----    ---    ------    ---------
     u0    unit    m      meters    length

$ ./mcvt.js meters

d    id    type    sym    name      dimension
-    --    ----    ---    ------    ---------
     u0    unit    m      meters    length

$

If multiple objects match your query, mcvt.js will list all matched objects. An asterisk in the first column indicates the default specified in the file disambiguation.json.

$ ./mcvt.js m

d    id       type    sym    name                   dimension
-    -----    ----    ---    -------------------    ---------
*    u0       unit    m      meters                 length
     u1300    unit    m      meters (wavelength)    time^-1

$

You can look up unit expressions as well.

$ ./mcvt.js u1/u0^0.5*u2^2

d    id                type    sym           name                                             dimension
-    --------------    ----    ----------    ---------------------------------------------    ------------------------
     u1/u0^0.5*u2^2    unit    kg/m⁰⸳⁵·s²    kilograms per square root meter square second    mass length^-0.5 time^-2

$

Example Unit Definitions

Base Units

Consider the definition of the meter:

"u0": {
	"symbol": "m",
	"name": {
		"en": {
			"1": "meter",
			"*": "meters"
		}
	},
	"dimension": {
		"length": 1
	}
}

This is read as "u0, the meter, is the base unit for length."

Derived Units Using Multiplication & Division

Consider the definition of the minute:

"u100": {
	"symbol": "min",
	"name": {
		"en": {
			"1": "minute",
			"*": "minutes"
		}
	},
	"multiplier": 60,
	"dimension": {
		"time": 1
	}
}

This is read as "u100, the minute, is 60 seconds (the base unit for time)."

Consider the definition of the liter:

"u107": {
	"symbol": "L",
	"name": {
		"en": {
			"1": "liter",
			"*": "liters"
		}
	},
	"divisor": 1000,
	"dimension": {
		"length": 3
	}
}

This is read as "u107, the liter, is one 1000th of a cubic meter (the base unit for length cubed, aka volume)."

Consider the definition of the knot:

"u163": {
	"symbol": "kn",
	"name": {
		"en": {
			"1": "knot",
			"*": "knots"
		}
	},
	"multiplier": 1852,
	"divisor": 3600,
	"dimension": {
		"length": 1,
		"time": -1
	}
}

This is read as "u163, the knot, is 1852/3600 meters per second (the base unit for length over time, aka velocity)."

Derived Units Using Reversible Operations

Consider the definition of degrees Fahrenheit:

"u900": {
	"symbol": "°F",
	"name": {
		"en": {
			"1": "degree Fahrenheit",
			"*": "degrees Fahrenheit"
		}
	},
	"instructions": "S32 M5 D9 A273.15",
	"dimension": {
		"temperature": 1
	}
}

This is read as "to convert from u900, degrees Fahrenheit, to kelvin (the base unit for temperature), subtract 32, multiply by 5, divide by 9, and add 273.15." (To convert in the other direction, the instructions must of course be performed in reverse.)

The following instructions are allowed in the instructions field:

instruction operation formula reverse operation
Aa add x' = x + a Sa
Sa subtract x' = xa Aa
Za subtract from x' = ax Za
Ma multiply x' = x × a Da
Da divide x' = x ÷ a Ma
Ga divide into x' = a ÷ x Ga
Pa power x' = xa Ra
Ra root x' = xa Pa
Xa exponential x' = ax La
La logarithm x' = loga x Xa
Ea natural exp x' = exa Na
Na natural log x' = ln (x + a) Ea
Ca circumference x' = x × π ÷ a Qa
Qa diameter x' = x × a ÷ π Ca
R2 sqrt x' = √x P2
R3 cbrt x' = ∛x P3
L2 log2 x' = log2 x X2
L10 log10 x' = log10 x X10
E0 exp x' = ex N0
E1 expm1 x' = ex − 1 N1
N0 log x' = ln x E0
N1 log1p x' = ln (x + 1) E1
C180 toRadians x' = x × π ÷ 180 Q180
Q180 toDegrees x' = x × 180 ÷ π C180
F1 sin x' = sin x V1
F2 cos x' = cos x V2
F3 tan x' = tan x V3
F4 cot x' = cot x V4
F5 sec x' = sec x V5
F6 csc x' = csc x V6
F7 sinh x' = sinh x V7
F8 cosh x' = cosh x V8
F9 tanh x' = tanh x V9
F10 coth x' = coth x V10
F11 sech x' = sech x V11
F12 csch x' = csch x V12
V1 asin x' = sin−1 x F1
V2 acos x' = cos−1 x F2
V3 atan x' = tan−1 x F3
V4 acot x' = cot−1 x F4
V5 asec x' = sec−1 x F5
V6 acsc x' = csc−1 x F6
V7 asinh x' = sinh−1 x F7
V8 acosh x' = cosh−1 x F8
V9 atanh x' = tanh−1 x F9
V10 acoth x' = coth−1 x F10
V11 asech x' = sech−1 x F11
V12 acsch x' = csch−1 x F12

Instructions such as integer divide, modulus, comparisons, gamma function, etc. are not available because they cannot be performed in reverse.

Scientific notation uses the underscore (_ instead of E or e) in the instructions field. An instruction such as M2E3 will be interpreted as x' = e2x−3, not x' = 2000x. To get the latter, use the instruction M2_3.

Derived Units Using JavaScript Functions

Consider the definition of the DIN #4 kinematic viscosity cup:

"n144": {
	"symbol": "s",
	"name": {
		"en": {
			"1": "second (DIN #4)",
			"*": "seconds (DIN #4)"
		}
	},
	"parser": "function (a) { return (a*4.57-452/a)/100 }",
	"formatter": "function (a) { a *= 100; return (Math.sqrt(a*a+8263)+a)/9.14 }",
	"dimension": {
		"length": 2,
		"time": -1
	}
}

This is read as "to convert from n144, seconds using DIN #4, to square meters per second (the base unit for kinematic viscosity), use the function f(a)=(a*4.57-452/a)/100; to convert in the other direction, multiply by 100, then use the function f(a)=(sqrt(a*a+8263)+a)/9.14."

The input to the parser function and the output of the formatter function need not be a number. Consider the definition of frequency described as musical pitch:

"z1": {
	"name": {
		"en": "note"
	},
	"datatype": "text",
	"parser": [
		"function (a) {",
		"  if (a.trim) a = a.trim();",
		"  if (!a) return NaN;",
		"  var i='CCDDEFFGGAAB'.indexOf(a[0].toUpperCase());",
		"  if (i < 0) return NaN;",
		"  a = a.substring(1);",
		"  if (a.trim) a = a.trim();",
		"  while (a) {",
		"    if (a[0] === '#' || a[0] === '\u266F') i++;",
		"    else if (a[0] === 'b' || a[0] === '\u266D') i--;",
		"    else if (a[0] !== '\u266E') break;"
		"    a = a.substring(1);",
		"  }",
		"  if (a.trim) a = a.trim();",
		"  if (!a.length || !isFinite(a)) a=4;",
		"  return (27.5 * Math.pow(2, (a * 12 + i - 9) / 12));",
		"}"
	],
	"formatter": [
		"function (a) {",
		"  if (!(a=Math.abs(a)) || !isFinite(a)) return '';",
		"  var i = Math.round(Math.log(a / 27.5) * 12 / Math.log(2) + 9);",
		"  a = Math.floor(i / 12);",
		"  return ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'][i - 12 * a] + a;",
		"}"
	],
	"dimension": {
		"time": -1
	}
}

The "datatype": "text" key-value pair indicates that the parser function takes a string and the formatter function returns a string. The parser and formatter functions in this example have been prettified for readability. Most functions in the actual data files are minimized.

It is generally recommended for JavaScript functions in unit definitions to use syntax as archaic as possible for maximum compatibility, hence the use of function (a) { ... } instead of a => { ... }, the check for a.trim, the use of var instead of const or let, the expression Math.log(a / 27.5) * 12 / Math.log(2) instead of Math.log2(a / 27.5) * 12, etc.

Example Test Cases

All Singing, All Dancing, All Inputs, All Outputs

Consider this (abbreviated) test case for length:

{
	"name": "inches",
	"u0_-3": 254000,
	"u0_-2": 25400,
	"u0_-1": 2540,
	"u0": 254,
	"u0_1": 25.4,
	"u0_2": 2.54,
	"u0_3": 0.254,
	"u207_-6": 10000000000,
	"u207": 10000,
	"epsilon": 1E-12
}

This test case states that 254000 millimeters (u0_-3), 25400 centimeters (u0_-2), 2540 decimeters (u0_-1), 254 meters (u0), 25.4 decameters (u0_1), 2.54 hectometers (u0_2), 0.254 kilometers (u0_3), 10000000000 microinches (u207_-6), and 10000 inches (u207) should all convert to each other. The actual number of conversions performed and verified is n2 where n is the number of key-value pairs. Conversions from a unit to itself are also included.

The epsilon key-value pair states that the maximum allowed difference |ab| between the actual result a and expected result b of a conversion shall be the greater of ( |a| + |b| ) · ε and ε (here ε = 10-12). This is often necessary because floating point has issues (if you know, you know). An epsilon of zero means a and b must be exactly equal (in the floating point sense, not the real number sense; if you know, you know); an epsilon of 1 means a and b may be anything as long as they have the same sign. Most test cases use an epsilon of 10-15 or 10-12. Some unlucky test cases may have an epsilon as large as 10-3; it is not recommended to have an epsilon larger than this.

One Half of an Input/Output

Consider this test case for numeral system:

{
	"name": "one half",
	"z102": "0.1",
	"z104": "0.2",
	"z106": "0.3",
	"z108": "0.4",
	"z110": "0.5",
	"z112": "0.6",
	"z116": "0.8",
	"z120": "0.A",
	"z136": "0.I",
	"z160": "0.U",
	"inputs": {
		"z120": "0.a",
		"z136": "0.i"
	},
	"outputs": {
		"z199": ""
	}
}

In this test case, the values of 0.a for z120 (vigesimal or base 20) and 0.i for z136 (sexatrigesimal or base 36) should only be tested as inputs; they should not be tested as outputs because the lowercase letters of the expected output would not match the uppercase letters of the actual output (0.A and 0.I respectively). Similarly, the empty string for z199 (Roman numerals) should only be tested as an output (given a non-integer, the Roman numeral conversion returns an empty string); it should not be tested as an input because an empty string would result in the actual output of an empty string for every other unit, which will not match any of the expected outputs.

About

Source data for the MultiConvert app

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published