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 @@
[](https://github.com/deftio/fr_math/actions/workflows/ci.yml)
[](#building-and-testing)
[](https://deftio.github.io/fr_math/)
-[](release_notes.md)
+[](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
-
-
-
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
-
-
+
+
+
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
@@ -246,7 +246,7 @@
History
built for graphics transforms on 16 MHz 68k Palm Pilots (it
shipped inside Trumpetsoft’s Inkstorm), then ported
forward to ARM, x86, MIPS, RISC-V, and various 8/16-bit embedded
-targets. v2.0.6 is the current release with a full test suite,
+targets. v2.0.7 is the current release with a full test suite,
bit-exact numerical specification, and CI on every push.
when it was written to run 2D graphics transforms on 16 MHz 68k
Palm Pilots for Trumpetsoft’s Inkstorm. It has since
been ported to ARM, x86, MIPS, RISC-V, and a menagerie of 8- and
-16-bit embedded targets. v2.0.0 is the first major revision with a
+16-bit embedded targets. v2.0.7 is the current release with a
full test suite, a bit-exact numerical specification, and CI on
every push.
diff --git a/release_notes.md b/release_notes.md
index 1de55e2..8a5f3bb 100644
--- a/release_notes.md
+++ b/release_notes.md
@@ -1,5 +1,38 @@
# FR_Math Release Notes
+## Version 2.0.7 (2026)
+
+README restructure, accuracy table cleanup, and expanded cross-compile support.
+
+### New: `FR_CORE_ONLY` convenience define
+
+A single `#define FR_CORE_ONLY` before including `FR_math.h` (or `-DFR_CORE_ONLY`
+on the command line) now strips both print helpers and wave generators in one step.
+It expands to `FR_NO_PRINT` + `FR_NO_WAVES` internally.
+
+### Accuracy table cleanup
+
+The LSB column has been removed from the accuracy table output. Percent error
+is the metric that matters to callers; LSB error is an implementation detail
+that varies with the chosen radix.
+
+### Expanded cross-compile targets
+
+- **RP2040 (Cortex-M0+)** and **STM32 (Cortex-M4)** added as named targets
+ in the Docker cross-build
+- **68HC11** toolchain added to the Docker image
+- Size table now shows two columns: **Core** (`-DFR_CORE_ONLY`) and **Full**
+- `docker/build_sizes.sh` outputs `build/sizes.csv` for automated patching
+- New `scripts/update_sizes.sh` auto-patches size tables into README, docs,
+ and HTML pages
+
+### README restructure
+
+Sections reordered: accuracy table moved above the size table to lead with
+the library's primary selling point. Size table now shows Core vs Full columns.
+
+---
+
## Version 2.0.6 (2026)
Accuracy improvements, lean-build options, and library cleanup.
diff --git a/scripts/accuracy_report.sh b/scripts/accuracy_report.sh
index a8426ee..1bd5745 100755
--- a/scripts/accuracy_report.sh
+++ b/scripts/accuracy_report.sh
@@ -89,8 +89,8 @@ patch_markdown() {
# Build replacement block: sentinel + header + separator + data + sentinel
local replacement
replacement=""$'\n'
- replacement+="| Function | Max err (LSB) | Max err (%) | Avg err (%) | Note |"$'\n'
- replacement+="|---|---:|---:|---:|---|"$'\n'
+ replacement+="| Function | Max err (%) | Avg err (%) | Note |"$'\n'
+ replacement+="|---|---:|---:|---|"$'\n'
replacement+="$DATA_ROWS"$'\n'
replacement+=""
@@ -123,8 +123,8 @@ patch_html() {
local cells
cells=$(echo "$line" | sed 's/^| //;s/ |$//' | sed 's/ | /\t/g')
local tr="
"
- while IFS=$'\t' read -r c1 c2 c3 c4 c5; do
- tr+="
${c1}
${c2}
${c3}
${c4}
${c5}
"
+ while IFS=$'\t' read -r c1 c2 c3 c4; do
+ tr+="
${c1}
${c2}
${c3}
${c4}
"
done <<< "$cells"
tr+="
"
if [ -n "$html_rows" ]; then
@@ -137,7 +137,7 @@ patch_html() {
local replacement
replacement=""$'\n'
replacement+="
"$'\n'
- replacement+="
Function
Max err (LSB)
Max err (%)
Avg err (%)
Note
"$'\n'
+ replacement+="
Function
Max err (%)
Avg err (%)
Note
"$'\n'
replacement+=""$'\n'
replacement+="$html_rows"$'\n'
replacement+=""$'\n'
diff --git a/scripts/update_sizes.sh b/scripts/update_sizes.sh
new file mode 100755
index 0000000..b696edd
--- /dev/null
+++ b/scripts/update_sizes.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+#
+# update_sizes.sh — read build/sizes.csv and patch the size table into
+# README.md, docs/building.md, and pages/guide/building.html.
+#
+# Usage:
+# scripts/update_sizes.sh # print table to stdout
+# scripts/update_sizes.sh --update # also patch the three doc files
+#
+# The table is delimited by sentinel comments:
+#
+# ...
+#
+#
+# Exit status: 0 on success, non-zero on missing CSV or extraction failure.
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+cd "${PROJECT_ROOT}"
+
+CSV="build/sizes.csv"
+MODE="print"
+
+for arg in "$@"; do
+ case "$arg" in
+ --update) MODE="update" ;;
+ -h|--help)
+ echo "Usage: scripts/update_sizes.sh [--update]"
+ echo " (no args) Read build/sizes.csv, print size table"
+ echo " --update Also patch README.md, docs/building.md, pages/guide/building.html"
+ exit 0
+ ;;
+ *) echo "Unknown option: $arg" >&2; exit 1 ;;
+ esac
+done
+
+if [ ! -f "${CSV}" ]; then
+ echo "ERROR: ${CSV} not found. Run docker/build_sizes.sh first." >&2
+ exit 1
+fi
+
+# -----------------------------------------------------------------------
+# 1. Read CSV and sort by width then full_bytes ascending
+# -----------------------------------------------------------------------
+
+# Skip header, sort numerically by field 2 (width) then field 4 (full_bytes)
+SORTED=$(tail -n +2 "${CSV}" | sort -t',' -k2,2n -k4,4n)
+
+if [ -z "${SORTED}" ]; then
+ echo "ERROR: No data rows in ${CSV}" >&2
+ exit 1
+fi
+
+# Build markdown data rows
+MD_ROWS=""
+while IFS=',' read -r target width core full; do
+ # Format bytes as X.X KB
+ fmt_kb() {
+ local val="$1"
+ if [[ "${val}" =~ ^[0-9]+$ ]]; then
+ awk "BEGIN { printf \"%.1f KB\", ${val}/1024.0 }"
+ else
+ echo "${val}"
+ fi
+ }
+ row="| ${target} | $(fmt_kb "${core}") | $(fmt_kb "${full}") |"
+ if [ -n "${MD_ROWS}" ]; then
+ MD_ROWS+=$'\n'
+ fi
+ MD_ROWS+="${row}"
+done <<< "${SORTED}"
+
+# Build full markdown table
+MD_TABLE=""$'\n'
+MD_TABLE+="| Target | Core | Full |"$'\n'
+MD_TABLE+="|--------|-----:|-----:|"$'\n'
+MD_TABLE+="${MD_ROWS}"$'\n'
+MD_TABLE+=""
+
+echo "${MD_TABLE}"
+
+if [ "${MODE}" != "update" ]; then
+ exit 0
+fi
+
+# -----------------------------------------------------------------------
+# 2. Patch markdown files
+# -----------------------------------------------------------------------
+patch_markdown() {
+ local file="$1"
+ if [ ! -f "$file" ]; then
+ echo " skip: $file not found" >&2
+ return
+ fi
+
+ perl -0777 -i -pe "
+ s{.*?}
+ {${MD_TABLE}}s
+ " "$file"
+
+ echo " patched: $file" >&2
+}
+
+patch_markdown "README.md"
+patch_markdown "docs/building.md"
+
+# -----------------------------------------------------------------------
+# 3. Patch HTML file (pages/guide/building.html)
+# -----------------------------------------------------------------------
+patch_html() {
+ local file="$1"
+ if [ ! -f "$file" ]; then
+ echo " skip: $file not found" >&2
+ return
+ fi
+
+ # Convert sorted CSV rows to HTML
rows
+ local html_rows=""
+ while IFS=',' read -r target width core full; do
+ fmt_kb() {
+ local val="$1"
+ if [[ "${val}" =~ ^[0-9]+$ ]]; then
+ awk "BEGIN { printf \"%.1f KB\", ${val}/1024.0 }"
+ else
+ echo "${val}"
+ fi
+ }
+ local tr="
${target}
$(fmt_kb "${core}")
$(fmt_kb "${full}")
"
+ if [ -n "$html_rows" ]; then
+ html_rows+=$'\n'
+ fi
+ html_rows+="${tr}"
+ done <<< "${SORTED}"
+
+ # Build the replacement block
+ local replacement
+ replacement=""$'\n'
+ replacement+="
report for sweeps at radixes 8, 12, 16, and 24.
Percent errors skip expected values near zero (|expected| < 0.01).
-
-
-
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
-
-
+
+
+
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
diff --git a/src/FR_math_2D.cpp b/src/FR_math_2D.cpp
index 573a09f..c9025b3 100644
--- a/src/FR_math_2D.cpp
+++ b/src/FR_math_2D.cpp
@@ -5,7 +5,7 @@
*
* @copy Copyright (C) <2001-2026>
* @author M A Chatterjee
- * @version 2.0.6 M. A. Chatterjee, cleaned up naming
+ * @version 2.0.7 M. A. Chatterjee, cleaned up naming
*
* This file contains integer math settable fixed point radix math routines for
* use on systems in which floating point is not desired or unavailable.
diff --git a/src/FR_math_2D.h b/src/FR_math_2D.h
index ba8456f..8f16330 100644
--- a/src/FR_math_2D.h
+++ b/src/FR_math_2D.h
@@ -3,7 +3,7 @@
*
* @copy Copyright (C) <2001-2026>
* @author M A Chatterjee
- * @version 2.0.6 M. A. Chatterjee, cleaned up naming
+ * @version 2.0.7 M. A. Chatterjee, cleaned up naming
*
* This file contains integer math settable fixed point radix math routines for
* use on systems in which floating point is not desired or unavailable.