This library is inspired by the excellent [[https://github.com/JuliaPhysics/Measurements.jl][Measurements.jl]] and can, for all intents and purposes, be considered a port of it (even if it doesn't yet support some of its features).
The basic gist is that one constructs =Measurement= objects for one's measurement values, which include uncertainties. This is done via one of two ways:
import measuremancer let x = 1.0 ± 0.1 # via unicode operator let y = measurement(2.0, 0.5)
Printing either of these yields a string representation using the =±= unicode operator:
1.0 ± 0.1
What makes this library actually useful however, is the ability to perform error propagation for the user.
import measuremancer let x = 1.0 ± 0.1 let y = 2.0 ± 0.5
print x + y print x * y print x - y print x / y
(=print= is a helper defined to print the expression in addition to the measurement. You may use =echo= as usual for regular printing). yields:
x + y: 3.0 ± 0.51 x * y: 2.0 ± 0.54 x - y: -1.0 ± 0.51 x / y: 0.50 ± 0.13
Error propagation is performed according to linear error propagation theory (or sometimes called "Gaussian error propagation"): https://en.wikipedia.org/wiki/Propagation_of_uncertainty
The library handles dependent variables correctly. This means things like:
import measuremancer let x = 1.0 ± 0.1 print x - x
print x / x
are handled correctly, resulting in:
x - x: 0.0 ± 0.0 x / x: 1.0 ± 0.0
meaning the resulting variable has no error associated to it.
We can also include more complicated expressions of course:
import measuremancer
proc gaus[T](x, μ, σ: T): T = result = 1.0 / sqrt(2 PI) exp(- ((x - μ)^2) / (2 * (σ^2)))
let x = 1.0 ± 0.1 let μ = 0.5 ± 0.05 let σ = 1.2 ± 0.2
print gaus(x, μ, σ)
Note: Be very careful using =^= or =**= for exponentiation. Notice the extra parenthesis around the =(x - μ)^2= term (and σ^2). That is, because at least as of Nim devel 1.7 it otherwise includes the =-= in the square!
gaus(x, μ, σ): 0.366 ± 0.0177
** Other distinct data types
Of course, we wouldn't be Nim if we couldn't also apply the whole logic to types other than =float=!
Feel free to perform error propagation on =unchained= types for example:
import unchained, measuremancer
let m = 1.0.kg ± 0.1.kg let a = 9.81.m•s⁻² ± 0.05.m•s⁻²
print m * a
which yields:
m * a: 9.81 ± 0.982 KiloGram•Meter•Second⁻²
NOTE: With units as complicated as =unchained's= types, there is still a chance of things breaking. Basic math (=*=, =/=, =+= and =-=) should work correctly now though.
** Compilation flags & Nim version notes
As of Nim version 1.6 any code using this library has to be compiled with:
--experimental:unicodeOperators
Feel free to create a =nim.cfg= in the directory of your program to avoid having to set this setting manually every time.
NOTE: This flag was not available in Nim 1.4 yet. Unicode operators simply weren't a thing. If you wish to use this library anyway, you can do so, simply by constructing =Measurements= using the =measurement= procedure. Further, in Nim >= 2.0, unicode operators are allowed by default. So, special compilation options are unneeded.
If compiled with ~-d:useCligen~, uncertain number pair formatting is the default [[https://github.com/c-blake/cligen/blob/master/cligen/strUt.nim][~cligen/strUt.fmtUncertain~]]. That uses the uncertainty to limit precision of both value & uncertainty to the same decimal place (3 decimals of the uncertainty by default, as per one common convention, but you can compile with ~-d:mmErrPrec=N~ to use ~N~ places).