Achieve compile-time unit correctness and avoid runtime surprises.
| Architecture \ OS | Linux | MacOS |
|---|---|---|
x86_64 |
✅ | ✅ |
arm64 |
✅ | ✅ |
| Branch name | Zig version |
|---|---|
master |
0.16.x, master |
zig-0.15 |
0.14.x, 0.15.x |
zig-0.13 |
0.13.x |
Use units as types, to document your functions, and convert to units of the same dimension:
const units = @import("unitz").quantities(f32);
const m = units.meter;
const s = units.second;
const kt = units.knot;
const @"km/h" = units.eval("km / h", .{});
fn aircraft_speed(distance: m, duration: s) kt {
const speed = distance.div(duration); // value is in m/s
const result: kt = .from(speed); // convert to target unit
std.debug.print("Speed: {} m/s = {} kt = {} km/h\n", .{
speed.val(),
result.val(),
speed.toVal(@"km/h"),
});
return result;
}A compilation error occurs when trying to perform an invalid conversion:
const J = units.joule;
const hp = units.imperial_horsepower;
const engine_power: hp = .init(130);
const energy = engine_power.to(J);Will result in the compilation error:
src/quantity.zig:84:60: error: Units are only interconvertible if they measure the same kind of dimension
comptime if (!unit_from.isCompatible(unit_to)) @compileError("Units are only interconvertible if they measure the same kind of dimension");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
foo.zig:8:35: note: called inline here
const energy = engine_power.to(J);
~~~~~~~~~~~~~~~^~~No conversion is done implicitly, the value stored in memory is exactly the one provided to the constructor.
This library does not provide all variations of standard units: meter and hour are provided, but kilometer and kilometer per hour is not. Instead, you can define any unit you want from its definition, using prefixes if needed:
const nanosecond = units.eval("ns", .{});
const @"kg/m3" = units.eval("kg / m^3", .{});
const kilowatthour = units.eval("kW * h", .{});
const Cal = units.eval("kcal", .{}); // large calorieconst std = @import("std");
const unitz = @import("unitz");
const q = unitz.quantities(f32);
const m = q.meter;
const kg = q.kilogram;
const lb = q.pound;
const cm = q.eval("cm", .{});
const @"kg/m²" = q.eval("kg / m^2", .{});
fn body_mass_index(height: m, weight: kg) @"kg/m²" {
return weight.div(height.pow(2));
}
pub fn main() void {
const height: cm = .init(162);
const weight: lb = .init(124);
const bmi = body_mass_index(height.to(m), weight.to(kg));
std.debug.print("BMI: {}", .{bmi.val()});
}We redefine slug and pound-force to show how it can be done
const u = @import("unitz").quantities(f32);
const slug = u.eval("32.174_049 * lb", .{});
const lbf = u.eval("ft * my_slug / s^2", .{ .my_slug = slug.unit });
const @"lbf.s" = u.eval("my_lbf * s", .{ .my_lbf = lbf.unit });
const @"N.s" = u.eval("N * s", .{});
const @"μs" = u.eval("us", .{});
fn compute_impulse(force: lbf, delta: @"μs") @"lbf.s" {
return .from(force.mul(delta)); // The .from converts to the target unit
// return force.mul(delta).to(@"lbf.s"); // equivalent
}
fn compute_trajectory(impulse: @"N.s") void {
// ...
}
pub fn main() void {
const force = lbf.init(123.0);
const delta = @"μs".init(45.0);
compute_trajectory(compute_impulse(force, delta)); // compilation error !
// Adding .to(@"N.s") will fix it:
// compute_trajectory(compute_impulse(force, delta).to(@"N.s"));
// compute_trajectory(.from(compute_impulse(force, delta))); // Also possible
}And just like that, you can avoid crashing into the atmosphere
Add the dependency in your build.zig.zon by running the following command:
zig fetch --save git+https://github.com/agagniere/unitz#masterAdd it to your exe in your build.zig:
exe.root_module.addImport("unitz", b.dependency("unitz", .{ .target = target, .optimize = optimize }).module("unitz"));Then you can import it from your code:
const unitz = @import("unitz");zig build docs
# Then serve locally, for example:
python -m http.server 8000 -d zig-out/docs
open "http://localhost:8000"zig fetch --save git+https://codeberg.org/InKryption/comath#main