Skip to content

deftio/qf_math

Repository files navigation

License CI Coverage Version GitHub

PlatformIO Arduino ESP-IDF

Quick Float Math (qf_math)

qf_math is a lightweight library for fast approximate math on IEEE-754 float — built for ARM Cortex-M, RISC-V, Xtensa/ESP32, and any 32-bit target. One translation unit, no heap, no dependencies, no FPU required (but benefits from one). Drop it into firmware for predictable-cost trig, log, exp, sqrt, hypot, waveforms, and ADSR — without pulling in full libm.

Need fixed point?: fr_math is the fixed-point (pure integer) cousin with the same API.
Docs: qf_math docs html pages for this library with algorithms, example code, metrics.

Float or fixed-point?

Depending on whether the target has hardware floating point support different chocies may be appropriate.

qf_math (this library) fr_math
Representation IEEE-754 float (32-bit) Caller-selectable fixed-radix int32_t (e.g. s15.16, s19.12, s23.8)
Best on Cortex-M4F/M7, ESP32, RP2040/RP2350 with FPU Cortex-M0/M3, 8/16-bit MCUs, no-FPU targets
Soft-float cost High (compiler inserts sw multiply/add) Zero (integer ALU only)
Dynamic range ~1e-38 to ~3e+38 Determined by radix (e.g. R=16: ±32K; R=12: ±512K; R=8: ±8M)
Typical accuracy < 0.002% FS (trig), < 0.001% rel (log/exp) < 0.008% FS (trig), < 0.4% rel (log/exp)
Code size ~10 KB (full), ~9 KB (lean) ~10 KB (full), ~5 KB (lean)

Accuracy — qf_math vs fr_math vs libm

Peak error from identical benchmark test suites. Both libraries use the approximation strategies (BAM phase, sub-step interpolation, poly interpolation, etc) with differences from quantization and dynamic range tradeoffs.

Function Metric libm qf_math fr_math
sin (rad) %FS 0 0.0019 0.0078
cos (rad) %FS 0 0.0019 0.0087
tan (rad) abs 0 0.30 0.051
asin abs rad 0 0.00047 0.00036
acos abs rad 0 0.00047 0.00036
atan2 abs rad 0 0.0013 0.00094
sqrt rel % 0 0.00047 1.19
log2 rel % 0 0.0016 0.32
exp rel % 0 0.00034 0.57
pow rel % 0 0.00038 0.087
hypot rel % 0 0.00047 0.000007

%FS = % of full-scale (±1 output for trig). rel % = relative error vs double reference. abs rad = absolute radians. Bold = closer to libm. libm column is the *f reference (effectively 0 error at this precision).

Speed vs libm — ESP32-S3 (with FPU)

Ratio of libm wall-clock time to library time for the same loop. > 1.0 = faster than sinf/sqrtf/etc. on that platform. ESP32-S3 has a single-precision FPU, so both qf_math and libm use hardware float — the advantage comes from simpler approximations with fewer branches.

Function qf_math fr_math Notes
sin (rad) 4.30× 1.32× qf_math: table + lerp; fr_math: integer table via float bridge
cos (rad) 4.39× 0.90×
tan (rad) 3.56× 1.31×
asin 1.10× 0.77×
acos 1.11× 0.77×
atan2 1.41× 0.70×
sqrt 1.07× 0.08× libm sqrtf is hard to beat (HW instruction on many cores)
exp 2.63× 1.88×
ln 2.20× 0.92×
hypot 2.51× 0.21× fr_math hypot goes through float bridge overhead

fr_math numbers include float↔fixed bridge overhead — native s32 / fix16_t calls in a pure-integer pipeline are faster. See compare/BENCHMARK_CROSSPLATFORM.md for full matrices including libfixmath, FastTrig, and ESP-DSP.

Quick start

#include "qf_math.h"

qf y   = qf_sin(1.0f);            /* radians */
qf len = qf_hypot(dx, dy);
qf dB  = 20.0f * qf_log10(v / ref);
Language C99 (float API, qf typedef), plus optional C++ header wrapper
Dependencies None at compile time for the library itself
License BSD-2-Clause — see LICENSE.txt

Version macros live in qf_math.h: QF_MATH_VERSION / QF_MATH_VERSION_HEX. When you bump the version string, update the Version badge at the top of this README to match.

For releases, prefer the version/release helpers so manifests, docs, badges, pages, and generated reports stay in sync:

python3 tools/qf_version.py show --format markdown
python3 tools/qf_version.py update 1.0.2
make make-release VERSION=1.0.2
make make-release RELEASE_ARGS="--dry-run --skip-docker --skip-peer-tests"

Why use this?

  • Deterministic building blocks for motor control, graphics-lite, sensor fusion, LED/audio synthesis, and telemetry pipelines.
  • Tight code size: one translation unit with shared lookup tables (see make size).
  • Explicit trade-offs: worst-case errors are documented in headers (e.g. trig ~3e-5 class behavior vs libm doubles).
  • No heap, minimal stack depth in normal use, integer-friendly helpers (QF_* macros, BAM phase, fixed-radix bridges).

API overview

Public surface is declared in src/qf_math.h:

  • Macros: clamps, interpolation, deg/rad/BAM, radix bridges (QF_TO_FR, …).
  • Trig: qf_sin, qf_cos, qf_tan, BAM-native variants, inverse trig.
  • Log/exp/pow: qf_log2, qf_ln, qf_log10, qf_pow2, qf_exp, qf_pow10, qf_pow.
  • Length: qf_sqrt, qf_hypot, qf_hypot_fast2, qf_hypot_fast8 (piecewise-linear magnitude).
  • Audio-ish: LFSR noise, PWM/square/saw/triangle waves, qf_adsr_* envelope.

Domain violations (sqrt of negative, log of non-positive) return QF_DOMAIN_ERROR.

Full reference: docs/API.md — Algorithms: docs/ALGORITHMS.mdFloat math tradeoffs (libm vs qf_math vs fixed-point vs DSP)

Repository layout

src/           qf_math.c, qf_math.h/.hpp — drop-in C library + C++ wrapper
test/          qf_math_test.c         — correctness vs libm on host
docs/          Markdown documentation (algorithms, API, fr_math, integration)
pages/         GitHub Pages site (compact HTML + CSS; deploy via Actions)
examples/      optional demos — ESP-IDF, LilyGO T-Display-S3, Raspberry Pi Pico 2
docker/        multi-arch toolchain image + cross size report
compare/       matrices vs other math libs + benchmark harness (see below)
tools/         extra shell helpers & qf_math vs libm micro-benchmark
build/         compiled binaries, CMake outputs, cloned deps (gitignored)

Build & test (host)

Requires a normal UNIX toolchain (gcc or clang) and -lm for tests and benchmarks only (references use sinf, logf, …).

Coverage gate

make coverage-check (also run in CI) rebuilds tests with gcov and fails unless every executable line in src/qf_math.c is covered. Use make coverage for the same build plus a human-readable gcov summary without enforcing 100%.

make help          # targets
make lib           # build/qf_math.o
make test          # build & run unit tests
make bench         # qf_math vs libm speed/accuracy (single TU bench)
make size          # object size report
make coverage      # gcov summary (optional gcov install; non-failing)
make coverage-check  # CI-style gate: 100% lines covered in src/qf_math.c
make clean

Cross-compile code sizes (Docker)

Like fr_math, this repo ships a docker/ image (Ubuntu + common 32/64-bit cross-compilers + Espressif Xtensa) to compile src/qf_math.c for several CPUs and emit the source-of-truth size matrix (build/docker_sizes.csv).

make docker-sizes           # CSV + compact compare/README.md summary
make docker-sizes-rebuild   # rebuild image, then CSV + summary

Details: docker/README.md.

Comparisons vs other math stacks (compare/)

Documentation tables live under compare/ (LIBRARIES.md for platforms/representation/footprint guidance, FUNCTION_MATRIX.md for symbol coverage vs libfixmath, fr_math, CMSIS-DSP, libm, …).

Target What it does
make compare-deps Shallow-clones libfixmath (MIT) and fr_math into build/compare/third_party/.
make compare Builds build/compare/benchmark_suite — accuracy + wall-clock for qf_math, libm, libfixmath (float bridge), fr_math (fixed-point bridge), and a Taylor poly baseline.
make compare-github-report Regenerates compare/BENCHMARK_REPORT.md for GitHub.
make mcu-benchmark-snapshot Flash MCU bench and rewrite compare/MCU_BENCHMARK_SNAPSHOT*.md via UART.
make benchmark-crossplatform Rewrite compare/BENCHMARK_CROSSPLATFORM.md — Host + MCU ratios from committed snapshots.

On silicon: examples/lilygo_t_display_s3_bench (PlatformIO), examples/esp32s3_benchmark (ESP-IDF), or examples/pico2_benchmark (Arduino, Pico 2 ARM/RISC-V) run the same benchmark_core.c loops on real hardware.

Packaging & integration

PlatformIO

Use library.json at the repo root: add this folder as a local library or publish following PlatformIO library format. Headers resolve from src/ (#include "qf_math.h" for C, #include "qf_math.hpp" for C++).

Espressif ESP-IDF

idf_component.yml registers the component for the ESP-IDF Component Manager. CMakeLists.txt wraps idf_component_register for src/qf_math.c with include path src/.

Full guide: docs/INTEGRATION.md

Documentation

File Purpose
docs/ Markdown documentation — algorithms, API reference, fr_math relationship, integration guide
pages/ GitHub Pages site (live at https://deftio.github.io/qf_math/)
compare/ Compare harness docs, benchmark reports, cross-platform matrices
llms.txt Compact index for LLM tooling / crawling
agents.md Conventions for coding agents working in this repo

Contributing & correctness

  • Prefer extending tests in test/qf_math_test.c when adjusting numerical behavior.
  • Keep binaries and fetched trees under build/ only.
  • Match existing comment and license style in touched files.

Credits

Author: M. A. Chatterjee (deftio at deftio dot com). See LICENSE.txt for full BSD-2-Clause text.

Third-party comparison tooling pulls libfixmath (MIT) and fr_math (BSD-2-Clause); neither is bundled in git—they populate only under build/compare/third_party/ when you run comparison targets.

About

Fast math library (float 32) for embedded systems. fast sin/cos/tan/asin/acos/atan (rad, degree, bam support) also exp,log,ln,pow,pow10, wave-gen, adsr. Same API as fixed point library deftio/fr_math

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors