Repository navigation
formula-cpp 0.2.0
formula-cpp 0.2.0
The second release of formula-cpp, a header-only C++23 library for traceable formulas: written
once in C++, with dimensions checked at compile time and units converted exactly, evaluated in exact
rational arithmetic, rendered as documentation, and traced step by step into an audit trail.
This release adds calculations whose values depend on each other, worksheets that recalculate only
what a change reaches, and decimals in traces and std::format. It also adds the first formulas
whose true values the exact layer cannot hold: logarithms, exponentials, and least-squares fits over
raw observations. Each such value is written only as the correctly rounded decimal its formula
declares, proved with integer arithmetic, so a trace still never shows a number that is not exact.
Documentation, with the guides, a gallery and the API reference:
https://lastrada-software.github.io/formula-cpp/
Requirements
- C++23 and CMake 3.23 or newer.
- CI builds and tests every change on MSVC
clandclang-cl(Windows), Clang and GCC 14
(Linux, with an UndefinedBehaviorSanitizer build) and Clang on macOS. - Minimum compiler versions are not settled yet.
What is new
- Calculations and worksheets:
define<Q>(expression)binds a quantity to its formula, and a
calculation's dependency graph is known at compile time. A mistake such as a cycle or a dimension
mismatch is one compile error. A worksheet computes each value only when asked, and recalculates
only what a change reaches. A derivation is re-run against the worksheet's current values, so it
is never stale. - Decimals and
std::format: traces, rendered formulas and derivations can show exact decimals,
or approximations that name their rounding mode and always carry≈. An opt-instd::formatter
handlesRationaland measured values; a bad format spec in a literal is a compile error. - Base dimensions an application declares:
base_dimension("EUR")declares a dimension the SI
does not have, so adding euros to yen, or to a bare ratio, fails to compile. No currencies are
shipped, and an exchange rate is data. - Units: watt, kilowatt, watt-hour and kilowatt-hour, with
dim::Power, and Fahrenheit. - Values the exact layer cannot hold:
rounded_output<"slope", Unit, Places, Mode>(fit)reports
an opaque operation's output at the precision the method declares. The rounded decimal is the
step's exact value, and its line says it was rounded. A rounding that cannot be decided is
Overflow, never a guess. The least-squares fits compute in wider integers, so, within those
integers' width, they answer this way where their exact route overflows 64 bits. - Logarithms and exponentials:
ln,log10andexptake a dimensionless argument, checked at
compile time. They are exact where the value is rational (ln 1, exp 0, log10 of a power of ten),
and otherwiseInexact.rounded_ln,rounded_log10androunded_expgive the correctly
rounded decimal at declared places, from an integer kernel. - A line through raw observations:
linear_least_squaresover two sets of observations, whose
number is data, gives the intercept, the slope, R² and the number of points. A fit of values that
are all equal is its own domain error. - Multiple regression:
multiple_least_squares(regressors(x1, ..., xK), y, citation), for one to
eight regressors, gives a constant, one coefficient per regressor in its own unit, R² and the
number of points. The exact route refuses a singular design exactly, by a zero pivot, not by a
tolerance.
Upgrading from 0.1.0
A minor release may break the public API before 1.0, and this one does in a few places. Each change
is listed under Changed in the CHANGELOG. The ones most likely to need an edit:
- New enumerators and members.
StepKindgains seven enumerators, appended.Dimension,
OpaqueCallInfo,OpaqueStepDataandOpaqueCallFailuregain members, so a structured binding
over one needs more names. AswitchoverStepKindthat names every enumerator and has no
defaultnow misses seven. - Argument-dependent lookup. An unqualified call of
ln,log10,exp,fraction_text,
number_textand a few others now also finds the library's function. A consumer's own function of
the same name for the same arguments makes such a call ambiguous. - Includes.
statistics.hppno longer brings inbinning.hpp,band.hpporlookup.hpp;
include them where they are used.
Using it
Install it with CMake, then consume it with find_package:
cmake -S . -B build -DFORMULA_INSTALL=ON -DCMAKE_INSTALL_PREFIX=/your/prefix
cmake --install buildfind_package(formula-cpp 0.2 CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE formula-cpp::formula-cpp)The package's compatibility is SameMinorVersion, so a request for 0.2 is not satisfied by 0.1 or
by 0.3. Because the library is header-only, copying include/ into your tree also works.
Known limits
- No stable API before 1.0. A minor release may change the public API. Every change is recorded
in the CHANGELOG. - 64-bit rational headroom (#1):
the exact route of some realistic laboratory statistics still overflows 64 bits. It reports an
overflow; it never gives a wrong number. The least-squares fits now answer at a declared
precision where their exact route overflows, within the width of their wider integers. The guide
"Numeric headroom" measures where each computation stops. - Units of single-value arithmetic in a trace
(#2): a computed single value, such
as a product or a quotient, is still shown in its coherent SI value with no unit symbol.
The full list of changes is in CHANGELOG.md.