Unit Conversion and Dimensional Analysis Library 3.6.1
A compile-time, header-only C++23 dimensional-analysis library
Loading...
Searching...
No Matches
FAQ

Common questions, and the reasoning behind the answers. For a hands-on start see getting started; for the full API on a page see the cheat sheet.

Why does 1_m / 2_m give 0?

Because 1_m and 2_m are meters<int>, and integer division truncates — exactly as 1 / 2 is 0 for plain int. The representation follows the literal: 5_m is meters<int>, 5.0_m is meters<double>. Write the decimal point (1.0_m / 2.0_m == 0.5) or name a floating-point type when you want fractional results. See CTAD and ADL.

What is the difference between .value() and .raw()?

For ordinary units they return the same number. They differ for ratio-dimensionless units such as percent: .raw() returns the stored value (the point count) while .value() returns the normalized fraction.

auto p = 50.0_pct;
p.raw(); // 50 — the number of points
p.value(); // 0.5 — the fraction

Use .value() when you want the quantity's value in its own units (the usual case), and .raw() when you specifically want the stored number. Both are distinct from .to<T>(), which casts to a chosen representation. operator() — the 2.x accessor — no longer exists; see the migration guide.

Why won't a plain double convert to a quantity (and vice versa)?

Because a bare number has no dimension, and letting it silently become a length (or a length silently become a number) is exactly the class of bug the library exists to prevent. Construct explicitly (meters d(5.0)), or extract explicitly (d.value()), at the boundary with non-units code. The one exception is a dimensionless quantity, which does convert to and from arithmetic types implicitly — because it genuinely has no dimension.

Why is a conversion I expected rejected at compile time?

Implicit conversions are allowed only when they are lossless. Converting feet to meters into an int-backed quantity would truncate, so it does not happen implicitly:

// meters<int> m = 12.0_in; // rejected: 12 in is 0.3048 m, lossy into an int
meters<double> m = 12.0_in; // fine: lossless
int truncated = (30.5_cm).to<int>(); // explicit: 30.5 -> 30 (truncates toward zero)

When you intend a narrowing conversion, ask for it explicitly with .to<T>() or unit_cast<T>(). Both apply a static_cast to the target type, so .to<int>() truncates toward zero (e.g. 2.7 -> 2, -2.7 -> -2); if you need rounding, round the value yourself first (see the rounding note in unit conversions). See type safety and unit conversions.

Can I use units with std::chrono?

Yes — time quantities convert implicitly to and from std::chrono::duration in both directions, and the two mix in a single expression via std::common_type. See chrono interop.

Caveat: conversion happens at assignment and argument boundaries, not inside a raw mixed expression. 1.5_min + std::chrono::seconds{30} does not compile; assign the chrono value to a units quantity first (or vice versa), then combine.

How do I change the default underlying type from double?

Define UNIT_LIB_DEFAULT_TYPE before including the library (for example to float on an embedded target). See configuration.

How do I use it without <iostream>?

Define UNIT_LIB_DISABLE_IOSTREAM (or set the UNITS_DISABLE_IOSTREAM CMake option). Arithmetic, conversions, and name()/abbreviation() still work; only the stream/string rendering is removed. See disabling iostream.

A literal or unit name collides with a system macro. What do I do?

Some platform headers define macros that clash with the SI abbreviations. #undef the offending macro before including units. The known ones: #undef pascal on Windows, and several single-letter #undefs (_U, _L, …) on some ARM toolchains. The Tesla literal is spelled _Te rather than _T for this reason (_T is a hardcoded Windows macro). See literals.

An unqualified math call is ambiguous. How do I fix it?

The unit-aware math functions are found by ADL, which considers them alongside anything else in scope. If a call is genuinely ambiguous, qualify it: units::hypot(a, b). Note also that signbit is provided as std::signbit (to win overload resolution against <cmath>), so call it qualified; isnan/isinf/isfinite are in units and work unqualified.

Does it cost anything at run time?

No. Conversions are compile-time ratio arithmetic, a conversion between equivalent representations emits no code, and a quantity is a trivially-copyable value the size of its underlying type. See efficiency.

What compilers and standard are required?

C++23, on GCC 13+, Clang 19+, or MSVC 2022. The 2.x series is the option for older toolchains. See migrating from 2.x.

Is it thread-safe? Does it throw?

Quantities are trivially-copyable value types with no shared state, so passing them by value across threads is safe (the usual rules apply to a shared mutable object, as for any value). The library's errors are compile-time; the operations do not throw.

How do I define my own unit?

One line with UNIT_ADD inside namespace units. See defining new units.

How do I cite this library?

Reference the project by name and repository, nholthaus/units (https://github.com/nholthaus/units), with the version tag you used.