From 7f9ba50eada1f6ce4100a083efb341660398f4fd Mon Sep 17 00:00:00 2001 From: deftio Date: Sun, 26 Apr 2026 13:54:48 -0700 Subject: [PATCH 1/2] updated docs for sizes, related ci for main taining size tables --- README.md | 141 ++++++++++++++++---------------- docker/Dockerfile | 1 + docker/build_sizes.sh | 159 ++++++++++++++++++++++++++----------- docs/README.md | 49 ++++++------ docs/building.md | 51 ++++++------ docs/releases.md | 15 +++- pages/guide/building.html | 49 ++++++------ pages/index.html | 44 +++++----- pages/releases.html | 19 ++++- release_notes.md | 33 ++++++++ scripts/accuracy_report.sh | 10 +-- scripts/update_sizes.sh | 158 ++++++++++++++++++++++++++++++++++++ src/FR_math.h | 9 ++- tests/test_tdd.cpp | 45 +++++------ 14 files changed, 532 insertions(+), 251 deletions(-) create mode 100755 scripts/update_sizes.sh diff --git a/README.md b/README.md index b3bce93..15d94f0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CI](https://github.com/deftio/fr_math/actions/workflows/ci.yml/badge.svg)](https://github.com/deftio/fr_math/actions/workflows/ci.yml) [![Coverage](https://img.shields.io/badge/coverage-98%25-brightgreen.svg)](#building-and-testing) [![Docs](https://img.shields.io/badge/docs-online-blue.svg)](https://deftio.github.io/fr_math/) -[![Version](https://img.shields.io/badge/version-2.0.6-blue.svg)](release_notes.md) +[![Version](https://img.shields.io/badge/version-2.0.7-blue.svg)](release_notes.md) # FR_Math: A C Language Fixed-Point Math Library for Embedded Systems @@ -18,51 +18,95 @@ into a single format. Pure C (C99/C11/C17) with an optional C++ 2D-transform wrapper. Compiles under Arduino. Zero dependencies beyond ``. +### Measured accuracy + +Errors below are measured at Q16.16 (s15.16). All functions accept any +radix — Q16.16 is just the reference point for the table. +Percent errors skip expected values near zero (|expected| < 0.01). + +At other radixes (3-bit, 24-bit, etc.) accuracy will differ due to the +number of fractional bits available. All functions support radix 0 to 30. + + +| Function | Max err (%) | Avg err (%) | Note | +|---|---:|---:|---| +| sin / cos | 0.7169 | 0.0100 | 65536-pt sweep + specials | +| tan | 0.7118 | 0.0162 | 65536-pt sweep (skip poles) | +| asin / acos | 0.7025 | 0.0105 | 65536-pt; sqrt approx near boundary | +| atan2 | 0.4953 | 0.0268 | 65536x5 radii; asin/acos+hypot_fast8 | +| atan | 0.2985 | 0.0159 | 20001-pt sweep [-10,10]; via FR_atan2 | +| sqrt | 0.0003 | 0.0000 | Round-to-nearest | +| log2 | 0.2479 | 0.0045 | 65-entry mantissa table | +| pow2 | 0.1373 | 0.0057 | 65-entry fraction table | +| ln, log10 | 0.0015 | 0.0004 | Via FR_MULK28 from log2 | +| exp | 0.0719 | 0.0051 | FR_MULK28 + FR_pow2 | +| exp_fast | 0.0719 | 0.0064 | Shift-only scaling | +| pow10 | 0.1163 | 0.0075 | FR_MULK28 + FR_pow2 | +| pow10_fast | 0.1163 | 0.0100 | Shift-only scaling | +| hypot (exact) | 0.0001 | 0.0000 | 64-bit intermediate | +| hypot_fast8 (8-seg) | 0.0977 | 0.0508 | Shift-only, no multiply | + + +### What's in the box + +| Area | Functions | +|---|---| +| Arithmetic | `FR_ADD`, `FR_SUB`, `FR_DIV`, `FR_DIV32`, `FR_MOD`, `FR_FixMuls`, `FR_FixMulSat`, `FR_CHRDX` | +| Utility | `FR_MIN`, `FR_MAX`, `FR_CLAMP`, `FR_ABS`, `FR_SGN` | +| Trig (integer deg) | `FR_Sin`, `FR_Cos`, `FR_Tan`, `FR_SinI`, `FR_CosI`, `FR_TanI` | +| Trig (radian/BAM) | `fr_sin`, `fr_cos`, `fr_tan`, `fr_sin_bam`, `fr_cos_bam`, `fr_sin_deg`, `fr_cos_deg` | +| Inverse trig | `FR_atan`, `FR_atan2`, `FR_asin`, `FR_acos` | +| Log / exp | `FR_log2`, `FR_ln`, `FR_log10`, `FR_pow2`, `FR_EXP`, `FR_POW10`, `FR_EXP_FAST`, `FR_POW10_FAST`, `FR_MULK28` | +| Roots | `FR_sqrt`, `FR_hypot`, `FR_hypot_fast8` | +| Wave generators | `fr_wave_sqr`, `fr_wave_pwm`, `fr_wave_tri`, `fr_wave_saw`, `fr_wave_tri_morph`, `fr_wave_noise` | +| Envelope | `fr_adsr_init`, `fr_adsr_trigger`, `fr_adsr_release`, `fr_adsr_step` | +| 2D transforms | `FR_Matrix2D_CPT` (mul, add, sub, det, inv, setrotate, XFormPtI, XFormPtI16) | +| Formatted output | `FR_printNumD`, `FR_printNumF`, `FR_printNumH`, `FR_numstr` | + ### Library size (FR_math.c only, `-Os`) Compiled object code sizes on select platforms (static test build). Your sizes may vary depending on optimization and linker settings. Sizes include all code and internal tables; everything is ROMable. -| Target | Code (text) | -|--------|-------------| -| ARM Thumb (Cortex-M0/M4) | 4.2 KB | -| RISC-V 32 (rv32imac) | 4.7 KB | -| RISC-V 64 | 4.5 KB | -| Xtensa LX106 (ESP8266) | 5.2 KB | -| 68k | 5.3 KB | -| ARM32 | 5.4 KB | -| x86-64 (GCC) | 5.7 KB | -| AArch64 (ARM64) | 6.0 KB | -| x86-64 (Clang) | 6.4 KB | -| x86-32 | 6.8 KB | -| PowerPC | 7.4 KB | -| MSP430 (16-bit) | 8.9 KB * | -| AVR (ATmega328P) | 10.6 KB * | - + +| Target | Core | Full | +|--------|-----:|-----:| +| RP2040 (Cortex-M0+) | 2.6 KB | 4.2 KB | +| STM32 (Cortex-M4) | 2.6 KB | 4.2 KB | +| RISC-V 32 (rv32imac) | 3.0 KB | 4.7 KB | +| ESP32 (Xtensa) | 3.5 KB | 5.2 KB | +| 68k | 3.5 KB | 5.3 KB | +| x86-64 (GCC) | 3.5 KB | 5.7 KB | +| x86-32 | 4.5 KB | 6.8 KB | +| MSP430 (16-bit) | 5.9 KB | 8.9 KB | +| 68HC11 | 10.8 KB | 16.0 KB | +| AVR (ATmega328P) | 7.0 KB | 10.6 KB | + + +Core = compiled with `-DFR_CORE_ONLY` (math only, no print, no waves). The optional 2D module adds ~1 KB. -\* MSP430 and AVR are 8/16-bit — every 32-bit operation expands to multiple instructions. +\* MSP430, 68HC11, and AVR are 8/16-bit — every 32-bit operation expands to multiple instructions. See [`docker/`](docker/) for the cross-compile setup. ### Lean build options -Two compile-time `#define` guards let you strip optional subsystems +Three compile-time `#define` guards let you strip optional subsystems for ROM-constrained targets. Define them before including `FR_math.h` (or pass `-D` on the compiler command line): | Define | What it removes | Typical savings | |---|---|---| +| `FR_CORE_ONLY` | Everything below (print + waves) | ~1.9 KB | | `FR_NO_PRINT` | `FR_printNumF`, `FR_printNumD`, `FR_printNumH`, `FR_numstr` | ~1.3 KB | | `FR_NO_WAVES` | `fr_wave_*` (6 shapes), `fr_adsr_*` (ADSR envelope), `FR_HZ2BAM_INC` | ~0.6 KB | -With both guards enabled the core math library (trig, inverse trig, log/exp, -sqrt, hypot) compiles to ~3.5 KB on x86-64 / clang -Os. On Thumb-2 this -would be roughly 2.6 KB. +`FR_CORE_ONLY` is a convenience shorthand that defines both +`FR_NO_PRINT` and `FR_NO_WAVES` in one step. ```c /* Example: headless sensor node — math only, no print, no audio */ -#define FR_NO_PRINT -#define FR_NO_WAVES +#define FR_CORE_ONLY #include "FR_math.h" ``` @@ -71,51 +115,6 @@ also strip any unused functions automatically, so these guards are most useful when you include the library as a single `.c` file or static archive without section-level dead-code elimination. -### Measured accuracy - -Errors below are measured at Q16.16 (s15.16). All functions accept any -radix — Q16.16 is just the reference point for the table. -Percent errors skip expected values near zero (|expected| < 0.01). - -At other radixes (3-bit, 24-bit, etc.) accuracy will differ due to the -number of fractional bits available. All functions support radix 0 to 30. - - -| Function | Max err (LSB) | Max err (%) | Avg err (%) | Note | -|---|---:|---:|---:|---| -| sin / cos | 7.5 | 0.7169 | 0.0100 | 65536-pt sweep + specials | -| tan | 38020.4 | 0.7118 | 0.0162 | 65536-pt sweep (skip poles) | -| asin / acos | 42.3 | 0.7025 | 0.0105 | 65536-pt; sqrt approx near boundary | -| atan2 | 63.3 | 0.4953 | 0.0268 | 65536x5 radii; asin/acos+hypot_fast8 | -| atan | 61.9 | 0.2985 | 0.0159 | 20001-pt sweep [-10,10]; via FR_atan2 | -| sqrt | 28.4 | 0.0003 | 0.0000 | Round-to-nearest | -| log2 | 10.5 | 0.2479 | 0.0045 | 65-entry mantissa table | -| pow2 | 220.4 | 0.1373 | 0.0057 | 65-entry fraction table | -| ln, log10 | 0.7 | 0.0015 | 0.0004 | Via FR_MULK28 from log2 | -| exp | 65.7 | 0.0719 | 0.0051 | FR_MULK28 + FR_pow2 | -| exp_fast | 195.5 | 0.0719 | 0.0064 | Shift-only scaling | -| pow10 | 143.4 | 0.1163 | 0.0075 | FR_MULK28 + FR_pow2 | -| pow10_fast | 581.9 | 0.1163 | 0.0100 | Shift-only scaling | -| hypot (exact) | 0.2 | 0.0001 | 0.0000 | 64-bit intermediate | -| hypot_fast8 (8-seg) | 59968.8 | 0.0977 | 0.0508 | Shift-only, no multiply | - - -### What's in the box - -| Area | Functions | -|---|---| -| Arithmetic | `FR_ADD`, `FR_SUB`, `FR_DIV`, `FR_DIV32`, `FR_MOD`, `FR_FixMuls`, `FR_FixMulSat`, `FR_CHRDX` | -| Utility | `FR_MIN`, `FR_MAX`, `FR_CLAMP`, `FR_ABS`, `FR_SGN` | -| Trig (integer deg) | `FR_Sin`, `FR_Cos`, `FR_Tan`, `FR_SinI`, `FR_CosI`, `FR_TanI` | -| Trig (radian/BAM) | `fr_sin`, `fr_cos`, `fr_tan`, `fr_sin_bam`, `fr_cos_bam`, `fr_sin_deg`, `fr_cos_deg` | -| Inverse trig | `FR_atan`, `FR_atan2`, `FR_asin`, `FR_acos` | -| Log / exp | `FR_log2`, `FR_ln`, `FR_log10`, `FR_pow2`, `FR_EXP`, `FR_POW10`, `FR_EXP_FAST`, `FR_POW10_FAST`, `FR_MULK28` | -| Roots | `FR_sqrt`, `FR_hypot`, `FR_hypot_fast8` | -| Wave generators | `fr_wave_sqr`, `fr_wave_pwm`, `fr_wave_tri`, `fr_wave_saw`, `fr_wave_tri_morph`, `fr_wave_noise` | -| Envelope | `fr_adsr_init`, `fr_adsr_trigger`, `fr_adsr_release`, `fr_adsr_step` | -| 2D transforms | `FR_Matrix2D_CPT` (mul, add, sub, det, inv, setrotate, XFormPtI, XFormPtI16) | -| Formatted output | `FR_printNumD`, `FR_printNumF`, `FR_printNumH`, `FR_numstr` | - ## Quick start ```bash @@ -210,7 +209,7 @@ The full docs ship in two forms — pick whichever fits how you read. FR_Math has been in service since 2000, originally built for graphics transforms on 16 MHz 68k Palm Pilots. It shipped inside Trumpetsoft's *Inkstorm* on PalmOS, then moved forward through ARM, x86, MIPS, -RISC-V, and various 8/16-bit embedded targets. v2.0.6 is the current +RISC-V, and various 8/16-bit embedded targets. v2.0.7 is the current release with a full test suite, bit-exact numerical specification, and CI on every push. @@ -226,5 +225,5 @@ BSD-2-Clause — see [LICENSE.txt](LICENSE.txt). ## Version -2.0.6 — see [release_notes.md](release_notes.md) for the v1 → v2 +2.0.7 — see [release_notes.md](release_notes.md) for the v1 → v2 migration guide, numerical fixes, and new functionality. diff --git a/docker/Dockerfile b/docker/Dockerfile index 6344151..4392617 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,6 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gcc-arm-none-eabi libnewlib-arm-none-eabi \ gcc-riscv64-unknown-elf \ gcc-12-m68k-linux-gnu \ + gcc-m68hc1x \ sdcc \ wget ca-certificates xz-utils bzip2 \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/build_sizes.sh b/docker/build_sizes.sh index 33cd328..f52ae88 100755 --- a/docker/build_sizes.sh +++ b/docker/build_sizes.sh @@ -1,12 +1,14 @@ #!/usr/bin/env bash # # build_sizes.sh — cross-compile FR_math.c for every supported target -# and report code (text section) sizes. +# and report code (text section) sizes in two configurations: +# Core = compiled with -DFR_CORE_ONLY (math only, no print, no waves) +# Full = compiled without FR_CORE_ONLY (everything included) # # Run inside the Docker container: # docker run --rm -v $(pwd):/src fr-math-sizes bash /src/docker/build_sizes.sh # -# Output: markdown table to stdout + build/size_table.md +# Output: build/sizes.csv + markdown table to stdout + build/size_table.md set -euo pipefail @@ -14,6 +16,7 @@ SRC="/src/src/FR_math.c" INC="-I/src/src" OUT="/src/build/size_report_docker" TABLE="/src/build/size_table.md" +CSV="/src/build/sizes.csv" mkdir -p "${OUT}" @@ -21,6 +24,7 @@ mkdir -p "${OUT}" # compile_gcc