Module Ymir_units.Unit
Units as exact values.
A symbol is a named dimension: m, kg, s, A, K, mol, cd, rad, or any a program declares (electron, adu). A symbol may carry a scope, a string naming the data set it belongs to. A term is a prime, π or a symbol. A unit is a product of terms raised to nonzero rational exponents. Its terms without symbols form its number, a positive real; a unit without symbols is dimensionless. Two units convert when their quotient is dimensionless, and the conversion from u to w is the number of u / w.
Every function on units is exact, and a unit has one form: kilo metre and int 1000 * metre are one unit, with one canonical text (Canonical text) that is its identity in equality, in the keys of compiled programs, in table metadata and in files.
Build units from The SI's units and constants, int, decimal, pi, symbol and the Algebra; leave a unit for a number with ratio.
let speed = Unit.(kilo metre / second) (* 1e3 m s^-1 *) let f = Unit.ratio Nx.float32 speed Unit.(metre / second) (* 1000. *)
type tThe type for units.
Constructors
int n is the number n, factored into primes exactly. Factoring is deterministic: the same n gives the same unit. Raises Invalid_argument if n < 1.
decimal s is the exact positive decimal s, as in "6.62607015e-34": digits ["." digits] ["e" ["-"] digits], with no sign, no + and no E. Raises Invalid_argument on a string outside this grammar, a zero value, or a mantissa, its digits read as one integer, of 262 or more.
symbol name is the symbol name. Raises Invalid_argument unless name matches [A-Za-z_][A-Za-z0-9_]* and is not "pi".
scoped ~scope name is the symbol name of the data set scope. It equals only a symbol with the same name and scope, so it converts only to itself. Raises Invalid_argument as symbol does, or if scope is empty.
Algebra
The units form an abelian group with rational exponents, and its identities hold exactly: (u * w) / w is u and root n (u ** n) is u. Each operation raises Invalid_argument when the numerator or denominator of a reduced exponent leaves int, as in "Unit.( ** ): the exponent of m leaves int", or when the numerator or denominator of the coefficient (Canonical text), in lowest terms, is past 4096 bits, as in "Unit.( * ): the coefficient's numerator is past 4096 bits". An exponent is a reduced fraction of two ints, min_int excluded; intermediate values never make an operation raise.
Conversion
ratio d u w is the conversion from u to w, the exact number v of u / w, rounded once to d:
- A float dtype rounds
vas IEEE 754 roundTiesToEven does ind, withd's subnormals and an exponent unbounded above. A complex dtype rounds as its component does and has a zero imaginary part. The result is exact in float64. - An integer dtype holds
vonly ifvis an integer in its range. Unsigned 32-bit and 64-bit values are their bit patterns.
The result is the same on every platform.
Raises Invalid_argument with:
"Unit.ratio: electron s^-1 does not convert to kg s^-1: their quotient keeps electron kg^-1"if the units do not convert;"Unit.ratio: the exponent of pi leaves int"if an exponent ofu / wleavesint;"Unit.ratio: the factor from 1e-35 kg s^-2 to kg s^-2 is 1e-35, which is 0 in float16"ifvrounds to 0, and withwhich is subnormal in float16if it rounds to a subnormal: a kernel on a device that flushes subnormals would compute with 0;which overflows float16ifvis above the largest finite value plus half its ulp, or equal to it when that value's last bit is odd: a format without infinity (float8_e4m3fn) raises where it would saturate;which is not an integerorwhich int8 does not holdfor an integer dtype;whose evaluation needs a natural wider than 65536 bitswhenv's exact evaluation does;"Unit.ratio: bool holds no factor"forNx.bool, and withbitforNx.bit.
Each message names d as Nx.Dtype.name writes it.
Terms
type term = The type for terms.
terms u is u's terms, each (t, num, den) with t's exponent num/den reduced, den >= 1 and num <> 0. They are in canonical order: primes ascending, then π, then symbols in byte order of name, then of scope, a symbol without scope first. A scope is ordered by its bytes as given, before the canonical text encodes them. terms one is [].
Canonical text
A unit's canonical text follows this grammar, with items separated by single spaces:
unit = "1" | item { " " item }
item = coeff | power
coeff = digits [ "e" [ "-" ] digits ] | digits "/" digits
power = base [ "^" [ "-" ] digits [ "/" digits ] ]
base = "pi" | digits | name [ "{" scope "}" ]The items are, in order:
- The coefficient Q: the product of the primes below 224 with an integer exponent. With
k = min (v2 Q) (v5 Q),Q * 10{^-k}writtenm, ormekwhenk <> 0, if it is an integerm, andn/din lowest terms otherwise. It is omitted when Q is 1. pi^rwhen π's exponentris not 0.- Each prime whose exponent is not an integer or that is at least 224, ascending.
- Each symbol, in the order of
terms. A scope is written in braces, with every byte outside[A-Za-z0-9._~:#/@+-]written%XXin upper-case hexadecimal.
A power whose exponent is 1 is its base; otherwise it is base^n, or base^n/d with d > 1, the sign on n. The whole unit is "1" when no item is written.
Unit.(kilo metre / second) 1e3 m s^-1 Unit.degree 1/180 pi rad Unit.hour 36e2 s Unit.(root 2 hertz) s^-1/2 Unit.planck 662607015e-42 kg m^2 s^-1
The text is injective: two units are equal iff their texts are. It is a stable format, since files and tables store units by their text.
of_string s is Ok u iff to_string u = s, and Error msg otherwise. When s's items denote a unit, msg names that unit's canonical text, as in {|"1000 m" is not canonical: the unit's canonical text is "1e3 m"|}; otherwise it names the reason, as in {|"m^" is not a unit's canonical text: expected digits at byte 2|}.
Before any arithmetic it rejects a coefficient whose digits, numerator or denominator are past 4096 bits, a power of ten e<k> with |k| > 4096, and an exponent or a base of 262 or more, so its time is bounded by s's length. A coefficient whose prime factors below 224 leave a cofactor of 262 or more is not canonical; it is rejected without naming a canonical text.
Predicates and comparisons
equal u w is true iff u and w are the same unit, which is iff to_string u = to_string w.
compare u w totally orders units, compatibly with equal: it is the byte order of their canonical texts.
Structure
val walk : ('a, 'b, 'd, 'e) Nx.Ptree.Walk.cursor -> t -> twalk c u is u, walked as one Walk.case holding its canonical text, and no tensor. Nx.Ptree.v walk describes it, and a compiled function that takes a unit compiles once per unit.
The SI
Units and constants are named by the SI Brochure's English name, in lower case with underscores. The radian is a symbol, so an angle is a dimension: steradian is radian ** 2, degree is π/180 rad and hertz is second ** -1, which is not rad s-1.
Base units
Derived units
Units accepted for use with the SI
The units the SI accepts whose value is exact and a multiple of an SI unit. The dalton is measured, and the neper, bel and decibel are logarithmic, so none of them is a unit here.
electronvolt is elementary_charge times volt: 1.602176634·10-19 J.
Prefixes
A prefix multiplies by a power of ten: kilo metre is int 1000 * metre.
Exact constants
The SI fixes these constants exactly, so they are units and stay exact through a formula until a conversion rounds it once. They belong to no CODATA release.
hbar is ℏ, planck / (int 2 * pi * radian): the action per radian. ℏω with ω in rad s-1 is an energy; ℏν with ν in Hz does not convert to one.
caesium_frequency is ΔνCs, the caesium 133 hyperfine transition frequency, 9192631770 s-1.