PicoMath is a header-only C++ library for math expression evaluation. PicoMath is very fast, simple, and customizable.
PicoMath was created to evaluate CSS-like expressions, like "100% - 20px"
, in the UI layout engine of my 2D game engine, Shinobit Engine. The main design goal of the library is to evaluate the expressions one single time as fast as possible, as layout usually happens when a scene is loaded, or a new UI component is shown, but they are not usually evaluated every frame.
Another goal was to make the library as simple as possible, the engine is compiled to WASM and size of the binary matters.
This library is currently used in the MMO browser game GoBattle.io
- Header only C++ library: easy integration in your project
- Very simple and fast
- Zero allocations in the hot path
- Uses C++17
std::string_view
to lookup variables without copying strings
- Uses C++17
- Single pass: The evaluation is performed while parsing.
- Built-in math functions (e.g.
cos(pi)
) - Custom units and percentages (e.g
100% - 10px
) - User defined variables (e.g.
(x + y) * (x + y)
) - User defined functions with multiple arguments (e.g.
avg(10, 20, 30)
) - Uses standard C++ containers
Just copy the file /include/picomath.hpp
in your project
Simple evaluation:
#include <picomath.hpp>
using namespace picomath;
PicoMath pm;
auto result = pm.evalExpression("sqrt(100 - 20)");
if (result.isOk()) {
double r = result.getResult();
...
}
Using variables:
#include <picomath.hpp>
using namespace picomath;
PicoMath pm;
auto &x = pm.addVariable("x");
x = 0.0;
while (x < 100.0) {
// Same expression evaluated with different values of `x`
auto result = pm.evalExpression("x * x * x");
if (result.isOk()) {
double r = result.getResult();
...
}
x += 1.0;
}
Using units:
#include <picomath.hpp>
using namespace picomath;
PicoMath pm;
pm.addUnit("km") = 1000.0;
pm.addUnit("cm") = 0.01;
auto result = pm.evalExpression("0.5km + 20cm");
if (result.isOk()) {
double r = result.getResult();
...
}
# build test binaries
make
# run tests
make test
# run bench tests
make bench
The default test binaries will be built in release mode. You can make Debug test binaries as well:
make clean
make debug
make test
PicoMath is pretty fast as it evaluates the expression without allocating memory.
Compared to a basic strtolower of the string (2 + 2) * 4 / 10 - 20.02
, using the method
std::transform(data.begin(), data.end(), data.begin(), [](unsigned char c) { return std::tolower(c); }));
the evaluation of the same string takes only around 25% more time. Please run the benchmark in your machine to validate the results.
Run on (12 X 2900 MHz CPU s)
CPU Caches:
L1 Data 32K (x6)
L1 Instruction 32K (x6)
L2 Unified 262K (x6)
L3 Unified 12582K (x1)
----------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------
Baseline tolower 43 ns 43 ns 16254876
Simple expression 54 ns 54 ns 12960515
PicoMath was designed to evaluate a expression in one pass, so to reevaluate the same expression requires parsing again the expression.
Some other libraries use bytecode or ASTs to improve the runtime cost of multiple evaluations of the same expression, but that increases complexity, size and number of allocations. In my tests comparing PicoMath to TinyExpr, you need to evaluate at least three times the same expression to get some benefit of the extra steps.
Contributors are welcome! ✨ please see CONTRIBUTING for more info.
PicoMath is licensed under a BSD 3-Clause
license, see LICENSE for more info.
Please let me know if you use the library in other projects and I can add the link in the readme.
This repository was created using the HPP-SKEL repository: https://github.com/mapbox/hpp-skel