& results) {
fprintf(f, "\n### Compiled size (clang -O2, macOS ARM)\n\n");
fprintf(f, "| | FR_math | libfixmath | lfm (no cache) |\n");
fprintf(f, "|---|---:|---:|---:|\n");
- fprintf(f, "| Code (text) | 6,888 B | 4,880 B | 5,444 B |\n");
- fprintf(f, "| Tables (ROM) | 834 B | 32 B | 32 B |\n");
- fprintf(f, "| **ROM total** | **7,722 B** | **4,912 B** | **5,476 B** |\n");
+ fprintf(f, "| Code (text) | 6,652 B | 4,880 B | 5,444 B |\n");
+ fprintf(f, "| Tables (ROM) | 818 B | 32 B | 32 B |\n");
+ fprintf(f, "| **ROM total** | **7,470 B** | **4,912 B** | **5,476 B** |\n");
fprintf(f, "| BSS / RAM | **0 B** | **112 KB** | **0 B** |\n");
fprintf(f, "\n");
- fprintf(f, "FR_math packs trig, inv-trig, log/ln/log10, exp/pow2/pow10, sqrt, hypot(3),\n");
- fprintf(f, "waves(6), ADSR, print into 7.5 KB ROM with zero RAM overhead.\n");
+ fprintf(f, "FR_math packs trig, inv-trig, log/ln/log10, exp/pow2/pow10, sqrt, hypot(2),\n");
+ fprintf(f, "waves(6), ADSR, print into 7.3 KB ROM with zero RAM overhead.\n");
fprintf(f, "libfixmath (trig, inv-trig, log/log2, exp, sqrt, mul/div, str) is 4.8 KB ROM\n");
fprintf(f, "but caches 112 KB of sin/exp LUTs in BSS at runtime.\n");
fprintf(f, "\n");
diff --git a/compare_lfm/comparison_results.json b/compare_lfm/comparison_results.json
index 73a81f7..adf0019 100644
--- a/compare_lfm/comparison_results.json
+++ b/compare_lfm/comparison_results.json
@@ -13,9 +13,9 @@
"double_reference": "std::sin",
"sweep": "65536-pt, [-pi, +pi]",
"speed": {
- "fr_math_ns_per_call": 2.5,
- "libfixmath_ns_per_call": 10.3,
- "fr_math_speedup": 4.06,
+ "fr_math_ns_per_call": 2.6,
+ "libfixmath_ns_per_call": 20.7,
+ "fr_math_speedup": 7.94,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -43,9 +43,9 @@
"double_reference": "std::cos",
"sweep": "65536-pt, [-pi, +pi]",
"speed": {
- "fr_math_ns_per_call": 2.3,
- "libfixmath_ns_per_call": 10.3,
- "fr_math_speedup": 4.51,
+ "fr_math_ns_per_call": 4.8,
+ "libfixmath_ns_per_call": 18.4,
+ "fr_math_speedup": 3.86,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -73,9 +73,9 @@
"double_reference": "std::tan",
"sweep": "65536-pt, [-1.2, 1.2] rad",
"speed": {
- "fr_math_ns_per_call": 4.2,
- "libfixmath_ns_per_call": 29.5,
- "fr_math_speedup": 7.02,
+ "fr_math_ns_per_call": 6.0,
+ "libfixmath_ns_per_call": 41.4,
+ "fr_math_speedup": 6.89,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -104,9 +104,9 @@
"double_reference": "std::asin",
"sweep": "65536-pt, [-0.999, 0.999]",
"speed": {
- "fr_math_ns_per_call": 9.0,
- "libfixmath_ns_per_call": 49.8,
- "fr_math_speedup": 5.55,
+ "fr_math_ns_per_call": 11.5,
+ "libfixmath_ns_per_call": 53.7,
+ "fr_math_speedup": 4.67,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -135,8 +135,8 @@
"sweep": "65536-pt, [-0.999, 0.999]",
"speed": {
"fr_math_ns_per_call": 8.4,
- "libfixmath_ns_per_call": 50.9,
- "fr_math_speedup": 6.05,
+ "libfixmath_ns_per_call": 50.4,
+ "fr_math_speedup": 5.97,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -164,9 +164,9 @@
"double_reference": "std::atan",
"sweep": "65536-pt, [-50, 50]",
"speed": {
- "fr_math_ns_per_call": 8.4,
- "libfixmath_ns_per_call": 11.4,
- "fr_math_speedup": 1.35,
+ "fr_math_ns_per_call": 8.0,
+ "libfixmath_ns_per_call": 11.2,
+ "fr_math_speedup": 1.41,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -194,8 +194,8 @@
"double_reference": "std::atan2",
"sweep": "65536-pt, 5 radii x 360 deg",
"speed": {
- "fr_math_ns_per_call": 16.1,
- "libfixmath_ns_per_call": 10.7,
+ "fr_math_ns_per_call": 15.9,
+ "libfixmath_ns_per_call": 10.5,
"fr_math_speedup": 0.66,
"faster": "libfixmath"
},
@@ -225,9 +225,9 @@
"double_reference": "std::sqrt",
"sweep": "65536-pt, [0.01, 100]",
"speed": {
- "fr_math_ns_per_call": 19.2,
- "libfixmath_ns_per_call": 20.7,
- "fr_math_speedup": 1.08,
+ "fr_math_ns_per_call": 18.6,
+ "libfixmath_ns_per_call": 19.8,
+ "fr_math_speedup": 1.06,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -255,9 +255,9 @@
"double_reference": "std::exp",
"sweep": "65536-pt, [-5, 5]",
"speed": {
- "fr_math_ns_per_call": 3.2,
- "libfixmath_ns_per_call": 65.2,
- "fr_math_speedup": 20.21,
+ "fr_math_ns_per_call": 3.1,
+ "libfixmath_ns_per_call": 67.6,
+ "fr_math_speedup": 22.02,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -286,8 +286,8 @@
"sweep": "65536-pt, [0.01, 100]",
"speed": {
"fr_math_ns_per_call": 8.8,
- "libfixmath_ns_per_call": 457.0,
- "fr_math_speedup": 51.86,
+ "libfixmath_ns_per_call": 479.3,
+ "fr_math_speedup": 54.70,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -315,9 +315,9 @@
"double_reference": "std::log2",
"sweep": "65536-pt, [0.01, 100]",
"speed": {
- "fr_math_ns_per_call": 8.9,
- "libfixmath_ns_per_call": 40.2,
- "fr_math_speedup": 4.50,
+ "fr_math_ns_per_call": 8.7,
+ "libfixmath_ns_per_call": 39.4,
+ "fr_math_speedup": 4.55,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -345,9 +345,9 @@
"double_reference": "double a*b",
"sweep": "65536-pt, a in [-50,50], b in [-2,2]",
"speed": {
- "fr_math_ns_per_call": 1.0,
- "libfixmath_ns_per_call": 1.3,
- "fr_math_speedup": 1.34,
+ "fr_math_ns_per_call": 0.9,
+ "libfixmath_ns_per_call": 1.2,
+ "fr_math_speedup": 1.33,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -376,8 +376,8 @@
"sweep": "65536-pt, a/b in [-50,50]/[0.5,50]",
"speed": {
"fr_math_ns_per_call": 0.9,
- "libfixmath_ns_per_call": 5.6,
- "fr_math_speedup": 6.21,
+ "libfixmath_ns_per_call": 5.2,
+ "fr_math_speedup": 5.98,
"faster": "fr_math"
},
"accuracy_vs_double": {
@@ -406,7 +406,7 @@
"double_reference": "std::hypot",
"sweep": "65536-pt, 5 radii x 360 deg",
"speed": {
- "fr_math_ns_per_call": 20.2
+ "fr_math_ns_per_call": 20.0
},
"accuracy_vs_double": {
"fr_math": {
@@ -461,8 +461,8 @@
"compiler": "clang -O2 (macOS ARM)",
"fr_math": {
"files": "FR_math.c (single file)",
- "functions": "trig(6), inv-trig(4), log/ln/log10, exp/pow2/pow10, exp_fast/pow10_fast, sqrt, hypot(3), waves(6), ADSR(4), print(4), format",
- "rom_bytes": 7722,
+ "functions": "trig(6), inv-trig(4), log/ln/log10, exp/pow2/pow10, exp_fast/pow10_fast, sqrt, hypot(2), waves(6), ADSR(4), print(4), format",
+ "rom_bytes": 7470,
"ram_bss_bytes": 0,
"note": "All tables in const ROM. Zero runtime allocation."
},
diff --git a/compare_lfm/comparison_summary.md b/compare_lfm/comparison_summary.md
index 63e9490..9169c50 100644
--- a/compare_lfm/comparison_summary.md
+++ b/compare_lfm/comparison_summary.md
@@ -43,20 +43,20 @@ All errors measured vs IEEE 754 double. Pct errors skip |ref| < 0.01.
| Function | FR_math | libfixmath | Speedup | Faster |
|----------|--------:|-----------:|--------:|--------|
-| sin | 2.5 | 10.3 | 4.06x | FR |
-| cos | 2.3 | 10.3 | 4.51x | FR |
-| tan | 4.2 | 29.5 | 7.02x | FR |
-| asin | 9.0 | 49.8 | 5.55x | FR |
-| acos | 8.4 | 50.9 | 6.05x | FR |
-| atan | 8.4 | 11.4 | 1.35x | FR |
-| atan2 | 16.1 | 10.7 | 0.66x | lfm |
-| sqrt | 19.2 | 20.7 | 1.08x | FR |
-| exp | 3.2 | 65.2 | 20.21x | FR |
-| ln | 8.8 | 457.0 | 51.86x | FR |
-| log2 | 8.9 | 40.2 | 4.50x | FR |
-| mul | 1.0 | 1.3 | 1.34x | FR |
-| div | 0.9 | 5.6 | 6.21x | FR |
-| hypot | 20.2 | --- | --- | FR only |
+| sin | 2.6 | 20.7 | 7.94x | FR |
+| cos | 4.8 | 18.4 | 3.86x | FR |
+| tan | 6.0 | 41.4 | 6.89x | FR |
+| asin | 11.5 | 53.7 | 4.67x | FR |
+| acos | 8.4 | 50.4 | 5.97x | FR |
+| atan | 8.0 | 11.2 | 1.41x | FR |
+| atan2 | 15.9 | 10.5 | 0.66x | lfm |
+| sqrt | 18.6 | 19.8 | 1.06x | FR |
+| exp | 3.1 | 67.6 | 22.02x | FR |
+| ln | 8.8 | 479.3 | 54.70x | FR |
+| log2 | 8.7 | 39.4 | 4.55x | FR |
+| mul | 0.9 | 1.2 | 1.33x | FR |
+| div | 0.9 | 5.2 | 5.98x | FR |
+| hypot | 20.0 | --- | --- | FR only |
| hypot_fast8 | 2.4 | --- | --- | FR only |
### Summary (13 head-to-head functions)
@@ -69,13 +69,13 @@ All errors measured vs IEEE 754 double. Pct errors skip |ref| < 0.01.
| | FR_math | libfixmath | lfm (no cache) |
|---|---:|---:|---:|
-| Code (text) | 6,888 B | 4,880 B | 5,444 B |
-| Tables (ROM) | 834 B | 32 B | 32 B |
-| **ROM total** | **7,722 B** | **4,912 B** | **5,476 B** |
+| Code (text) | 6,652 B | 4,880 B | 5,444 B |
+| Tables (ROM) | 818 B | 32 B | 32 B |
+| **ROM total** | **7,470 B** | **4,912 B** | **5,476 B** |
| BSS / RAM | **0 B** | **112 KB** | **0 B** |
-FR_math packs trig, inv-trig, log/ln/log10, exp/pow2/pow10, sqrt, hypot(3),
-waves(6), ADSR, print into 7.5 KB ROM with zero RAM overhead.
+FR_math packs trig, inv-trig, log/ln/log10, exp/pow2/pow10, sqrt, hypot(2),
+waves(6), ADSR, print into 7.3 KB ROM with zero RAM overhead.
libfixmath (trig, inv-trig, log/log2, exp, sqrt, mul/div, str) is 4.8 KB ROM
but caches 112 KB of sin/exp LUTs in BSS at runtime.
diff --git a/docs/README.md b/docs/README.md
index d30d8c9..a678336 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -84,6 +84,33 @@ radix — Q16.16 is just the reference point for the table. See the
Every function is covered by the TDD characterization suite in
[`tests/test_tdd.cpp`](../tests/test_tdd.cpp).
+## Lean build options
+
+Two 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_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.
+
+```c
+/* Example: headless sensor node — math only, no print, no audio */
+#define FR_NO_PRINT
+#define FR_NO_WAVES
+#include "FR_math.h"
+```
+
+With `-ffunction-sections` and linker `--gc-sections`, the linker will
+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.
+
## Why fixed-point, in 2026?
Most application code today has an FPU and can use `float` freely.
@@ -133,7 +160,7 @@ s32 two = I2FR(2, R); /* 2.0 → raw 131072 */
* UPPERCASE FR_ names are macros — they expand inline with no call
* overhead, and the compiler can constant-fold them. Use these for
* conversions and simple arithmetic:
- * I2FR, FR2I, FR_NUM, FR_ADD, FR_MUL, FR_DIV, FR_ABS, FR_EXP ...
+ * I2FR, FR2I, FR_NUM, FR_ADD, FR_DIV, FR_ABS, FR_CHRDX, FR_EXP ...
*
* MixedCase FR_ names are functions — they contain loops, tables, or
* multi-step algorithms where inlining would waste ROM:
@@ -198,7 +225,7 @@ script.
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*), then ported forward to ARM, x86, MIPS,
-RISC-V, and various 8/16-bit embedded targets. v2.0.2 is the current
+RISC-V, and various 8/16-bit embedded targets. v2.0.6 is the current
release with a full test suite, bit-exact numerical
specification, and CI on every push.
diff --git a/docs/building.md b/docs/building.md
index 2265b69..e7c805e 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -95,7 +95,7 @@ binaries to keep compile times low:
| Binary | What it checks |
| --- | --- |
-| `test_basic` | Radix conversions, `FR_ADD`, `FR_MUL`, rounding. |
+| `test_basic` | Radix conversions, `FR_ADD`, `FR_FixMuls`, rounding. |
| `test_trig` | Integer-degree trig (`FR_Sin` et al.). |
| `test_trig_radians` | Radian / BAM trig and the v2 `fr_sin` API. |
| `test_log_exp` | Log base 2 / ln / log10 and their inverses. |
@@ -184,6 +184,32 @@ references are linked, so real flash usage will be smaller.
| GCC 68HC11 | 17,331 |
+### Lean build options
+
+Two 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_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.
+
+```c
+/* Example: headless sensor node — math only, no print, no audio */
+#define FR_NO_PRINT
+#define FR_NO_WAVES
+#include "FR_math.h"
+```
+
+With `-ffunction-sections` and linker `--gc-sections`, the linker will
+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.
+
To regenerate this table, run the Docker cross-build
(requires the [xelp](https://github.com/deftio/xelp) Docker image):
diff --git a/docs/releases.md b/docs/releases.md
index 2f6e11c..2568ae7 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -4,6 +4,18 @@ Release highlights. For the full per-symbol change log, see
[release_notes.md](https://github.com/deftio/fr_math/blob/master/release_notes.md)
in the repo.
+## v2.0.6 — 2026
+
+Accuracy improvements, lean-build options, library cleanup.
+
+- **FR_acos boundary fix** — 12x better accuracy near ±1.0 via deferred quantization
+- **FR_atan2 rewrite** — asin/acos + hypot_fast8, 0.41% peak error (was 20% in libfixmath)
+- **Lean build guards** — `FR_NO_PRINT` (~1.3 KB) and `FR_NO_WAVES` (~0.6 KB) for ROM-constrained targets
+- **Removed FR_hypot_fast** (4-segment) — FR_hypot_fast8 is strictly better; 4-seg was dead weight
+- libfixmath comparison benchmark added to repo (`compare_lfm/`)
+
+---
+
## v2.0.5 — 2026
Release pipeline fixes. Fixed squash-merge divergence handling and
diff --git a/keywords.txt b/keywords.txt
index 483da8b..1ab2703 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -28,7 +28,6 @@ FR_log10 KEYWORD2
FR_pow2 KEYWORD2
FR_sqrt KEYWORD2
FR_hypot KEYWORD2
-FR_hypot_fast KEYWORD2
FR_hypot_fast8 KEYWORD2
FR_printNumF KEYWORD2
FR_printNumD KEYWORD2
diff --git a/llms.txt b/llms.txt
index 33e0b78..fe5ee3b 100644
--- a/llms.txt
+++ b/llms.txt
@@ -116,7 +116,6 @@ FR_POW10_FAST(input, radix)
```c
s32 FR_sqrt(s32 input, u16 radix);
s32 FR_hypot(s32 x, s32 y, u16 radix); // exact, 64-bit intermediate
-s32 FR_hypot_fast(s32 x, s32 y); // 4-segment, 0.34% error, no multiply
s32 FR_hypot_fast8(s32 x, s32 y); // 8-segment, 0.10% error, no multiply
```
@@ -171,6 +170,13 @@ make examples # build example program
make clean # remove build artifacts
```
+## Lean build options
+
+Define before including FR_math.h to exclude optional subsystems:
+
+- `FR_NO_PRINT` — removes FR_printNumF/D/H and FR_numstr (~1.3 KB saved)
+- `FR_NO_WAVES` — removes fr_wave_*, fr_adsr_*, FR_HZ2BAM_INC (~0.6 KB saved)
+
## Platform support
Tested on: AVR (Arduino), ARM Cortex-M0/M4, ESP32 (Xtensa), RISC-V,
diff --git a/pages/guide/building.html b/pages/guide/building.html
index f63195f..12750f6 100644
--- a/pages/guide/building.html
+++ b/pages/guide/building.html
@@ -113,7 +113,7 @@ The test suite
| Binary | What it checks |
-test_basic | Radix conversions, FR_ADD, FR_MUL, rounding. |
+test_basic | Radix conversions, FR_ADD, FR_FixMuls, rounding. |
test_trig | Integer-degree trig (FR_Sin et al.). |
test_trig_radians | Radian / BAM trig and the v2 fr_sin API. |
test_log_exp | Log base 2 / ln / log10 and their inverses. |
@@ -209,6 +209,33 @@ Code size (.text section, compiled with -Os)
+Lean build options
+
+Two 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_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.
+
+/* Example: headless sensor node — math only, no print, no audio */
+#define FR_NO_PRINT
+#define FR_NO_WAVES
+#include "FR_math.h"
+
+With -ffunction-sections and linker --gc-sections,
+the linker will 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.
+
To regenerate this table, run the Docker cross-build
(requires the xelp Docker image):
diff --git a/pages/index.html b/pages/index.html
index 5e0e3f4..35c81d2 100644
--- a/pages/index.html
+++ b/pages/index.html
@@ -96,6 +96,34 @@ What’s in the box
TDD
characterization suite in the repo.
+Lean build options
+
+Two 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_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.
+
+/* Example: headless sensor node — math only, no print, no audio */
+#define FR_NO_PRINT
+#define FR_NO_WAVES
+#include "FR_math.h"
+
+With -ffunction-sections and linker --gc-sections,
+the linker will 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.
+
Why fixed-point, in 2026?
Most application code today has an FPU and can use float
@@ -149,7 +177,7 @@
Quick taste
* UPPERCASE FR_ names are macros — they expand inline with no call
* overhead, and the compiler can constant-fold them. Use these for
* conversions and simple arithmetic:
- * I2FR, FR2I, FR_NUM, FR_ADD, FR_MUL, FR_DIV, FR_ABS, FR_EXP ...
+ * I2FR, FR2I, FR_NUM, FR_ADD, FR_DIV, FR_ABS, FR_CHRDX, FR_EXP ...
*
* MixedCase FR_ names are functions — they contain loops, tables, or
* multi-step algorithms where inlining would waste ROM:
@@ -218,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.2 is the current release with a full test suite,
+targets. v2.0.6 is the current release with a full test suite,
bit-exact numerical specification, and CI on every push.
License
diff --git a/pages/releases.html b/pages/releases.html
index d87abe0..9c15d09 100644
--- a/pages/releases.html
+++ b/pages/releases.html
@@ -21,6 +21,20 @@ Releases
release_notes.md
in the repo.
+v2.0.6 — 2026
+
+Accuracy improvements, lean-build options, library cleanup.
+
+
+ - FR_acos boundary fix — 12x better accuracy near ±1.0 via deferred quantization
+ - FR_atan2 rewrite — asin/acos + hypot_fast8, 0.41% peak error (was 20% in libfixmath)
+ - Lean build guards —
FR_NO_PRINT (~1.3 KB) and FR_NO_WAVES (~0.6 KB) for ROM-constrained targets
+ - Removed FR_hypot_fast (4-segment) — FR_hypot_fast8 is strictly better; 4-seg was dead weight
+ - libfixmath comparison benchmark added to repo (
compare_lfm/)
+
+
+
+
v2.0.5 — 2026
Release pipeline fixes. Fixed squash-merge divergence handling and
diff --git a/release_notes.md b/release_notes.md
index 793bb03..1de55e2 100644
--- a/release_notes.md
+++ b/release_notes.md
@@ -1,5 +1,42 @@
# FR_Math Release Notes
+## Version 2.0.6 (2026)
+
+Accuracy improvements, lean-build options, and library cleanup.
+
+### Accuracy & algorithms
+
+- **FR_acos boundary fix**: deferred quantization computes `1-x` at the
+ caller's radix instead of r15, giving 12x better accuracy near ±1.0
+ (max LSB error 512.6 → 42.3)
+- **FR_atan2 rewrite**: uses asin/acos + hypot_fast8 with octant
+ switching for well-conditioned results everywhere (0.41% peak vs
+ 20% for libfixmath)
+
+### Lean build options
+
+Two new compile-time `#define` guards strip optional subsystems for
+ROM-constrained targets:
+
+| Define | Removes | Savings |
+|---|---|---|
+| `FR_NO_PRINT` | `FR_printNumF/D/H`, `FR_numstr` | ~1.3 KB |
+| `FR_NO_WAVES` | `fr_wave_*`, `fr_adsr_*`, `FR_HZ2BAM_INC` | ~0.6 KB |
+
+With both guards the core math library compiles to ~3.5 KB on x86-64
+(clang -Os), roughly 2.6 KB on Thumb-2.
+
+### Removed
+
+- **FR_hypot_fast** (4-segment) deleted — FR_hypot_fast8 (8-segment)
+ is strictly better in both accuracy (0.10% vs 0.34%) and is used
+ internally by FR_atan2. The 4-segment variant was dead weight.
+
+### Other
+
+- libfixmath comparison benchmark (`compare_lfm/`) added to repo
+- Documentation updated across all markdown and HTML pages
+
## Version 2.0.5 (2026)
Release pipeline fixes. No functional changes to the math library.
diff --git a/src/FR_math.c b/src/FR_math.c
index 9edfdf3..7f3600e 100644
--- a/src/FR_math.c
+++ b/src/FR_math.c
@@ -664,6 +664,7 @@ s32 FR_log10(s32 input, u16 radix, u16 output_radix)
return FR_MULK28(r, FR_krLOG2_10_28);
}
+#ifndef FR_NO_PRINT
/***************************************
* FR_printNumD - write a decimal integer with space padding.
*
@@ -928,6 +929,7 @@ s32 FR_numstr(const char *s, u16 radix)
return neg ? -result : result;
}
+#endif /* FR_NO_PRINT */
/*=======================================================
* Square root and hypot
@@ -1094,6 +1096,7 @@ s32 FR_hypot_fast8(s32 x, s32 y)
}
}
+#ifndef FR_NO_WAVES
/*=======================================================
* Wave generators — synth-style fixed-shape waveforms.
*
@@ -1365,3 +1368,4 @@ s16 fr_adsr_step(fr_adsr_t *env)
return (s16)out;
}
}
+#endif /* FR_NO_WAVES */
diff --git a/src/FR_math.h b/src/FR_math.h
index fc3283d..3495c8a 100644
--- a/src/FR_math.h
+++ b/src/FR_math.h
@@ -455,6 +455,19 @@ static inline s32 FR_div_rnd(s64 num, s32 den) {
#define FR_EXP_FAST(input, radix) (FR_pow2(FR_SLOG2E(input), radix))
#define FR_POW10_FAST(input, radix) (FR_pow2(FR_SLOG2_10(input), radix))
+/*===============================================
+ * Formatted output and string parsing
+ *
+ * Define FR_NO_PRINT before including this header to exclude all
+ * print/format functions from compilation. This saves ~1.7 KB of ROM
+ * on targets that don't need human-readable output (e.g. headless
+ * sensor nodes, DSP-only firmware).
+ *
+ * #define FR_NO_PRINT
+ * #include "FR_math.h"
+ */
+#ifndef FR_NO_PRINT
+
/* printing family of functions */
int FR_printNumF(int (*f)(char), s32 n, int radix, int pad, int prec); /* print fixed radix num as floating point e.g. -12.34" */
int FR_printNumD(int (*f)(char), int n, int pad); /* print decimal number with optional padding e.g. " 12" */
@@ -463,6 +476,8 @@ static inline s32 FR_div_rnd(s64 num, s32 den) {
/* string-to-fixed-point parser (inverse of FR_printNumF) */
s32 FR_numstr(const char *s, u16 radix);
+#endif /* FR_NO_PRINT */
+
/*===============================================
* Square root and hypot
*
@@ -488,6 +503,19 @@ static inline s32 FR_div_rnd(s64 num, s32 den) {
*/
s32 FR_hypot_fast8(s32 x, s32 y);
+/*===============================================
+ * Wave generators and ADSR envelope
+ *
+ * Define FR_NO_WAVES before including this header to exclude all
+ * waveform generators (square, pulse, triangle, saw, noise) and the
+ * ADSR envelope from compilation. This saves ~400 B of ROM on targets
+ * that only need math/trig and don't do audio synthesis.
+ *
+ * #define FR_NO_WAVES
+ * #include "FR_math.h"
+ */
+#ifndef FR_NO_WAVES
+
/*===============================================
* Wave generators — synth-style fixed-shape waveforms.
*
@@ -568,6 +596,8 @@ typedef struct fr_adsr_s {
void fr_adsr_release(fr_adsr_t *env);
s16 fr_adsr_step(fr_adsr_t *env);
+#endif /* FR_NO_WAVES */
+
#ifdef __cplusplus
} // extern "C"
From d167309ee813b60de28e543136b9ea4106fd56bb Mon Sep 17 00:00:00 2001
From: deftio
Date: Tue, 21 Apr 2026 23:10:07 -0700
Subject: [PATCH 4/6] v2.0.6 release : updated accuracy, udpated docs,
selective builds
---
README.md | 24 ++++++++++++++----------
docs/building.md | 32 ++++++++++++++++----------------
pages/guide/building.html | 32 ++++++++++++++++----------------
3 files changed, 46 insertions(+), 42 deletions(-)
diff --git a/README.md b/README.md
index e8853da..19e958a 100644
--- a/README.md
+++ b/README.md
@@ -26,18 +26,22 @@ include all code and internal tables; everything is ROMable.
| Target | Code (text) |
|--------|-------------|
-| Cortex-M0 (Thumb-1) | 4.2 KB |
-| Cortex-M4 (Thumb-2) | 4.1 KB |
-| ESP32 (Xtensa) | 4.6 KB |
-| 68k | 5.5 KB |
-| x86-64 | 5.8 KB |
-| RISC-V 32 (rv32im) | 6.5 KB |
-| x86-32 | 7.2 KB |
-| MSP430 (16-bit) | 8.4 KB |
-| 8051 (SDCC) | 20.4 KB * |
+| 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 * |
The optional 2D module adds ~1 KB.
-\* 8051 and MSP430 are 8/16-bit — every 32-bit operation expands to multiple instructions.
+\* MSP430 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
diff --git a/docs/building.md b/docs/building.md
index e7c805e..db13bb7 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -166,22 +166,22 @@ references are linked, so real flash usage will be smaller.
| Target | .text (bytes) |
|---|---:|
-| GCC ARM32 Thumb | 4,530 |
-| GCC RISC-V (rv64) | 4,830 |
-| GCC RISC-V (rv32) | 5,068 |
-| GCC Xtensa LX106 (ESP8266) | 5,548 |
-| GCC ARM32 | 5,820 |
-| GCC m68k | 5,626 |
-| GCC x86-64 | 6,130 |
-| Clang x86-64 | 6,728 |
-| GCC AArch64 (ARM64) | 6,396 |
-| GCC x86-32 | 7,362 |
-| GCC PowerPC | 7,940 |
-| GCC MSP430 | 9,958 |
-| TCC x86 | 10,190 |
-| GCC AVR5 (ATmega328P) | 11,912 |
-| GCC AVR ATtiny85 | 12,410 |
-| GCC 68HC11 | 17,331 |
+| GCC ARM32 Thumb | 4,278 |
+| GCC RISC-V (rv64) | 4,574 |
+| GCC RISC-V (rv32) | 4,820 |
+| GCC Xtensa LX106 (ESP8266) | 5,317 |
+| GCC m68k | 5,410 |
+| GCC ARM32 | 5,504 |
+| GCC x86-64 | 5,857 |
+| GCC AArch64 (ARM64) | 6,112 |
+| Clang x86-64 | 6,555 |
+| GCC x86-32 | 6,947 |
+| GCC PowerPC | 7,540 |
+| GCC MSP430 | 9,146 |
+| TCC x86 | 9,887 |
+| GCC AVR5 (ATmega328P) | 10,806 |
+| GCC AVR ATtiny85 | 11,382 |
+| GCC 68HC11 | 16,392 |
### Lean build options
diff --git a/pages/guide/building.html b/pages/guide/building.html
index 12750f6..f870b20 100644
--- a/pages/guide/building.html
+++ b/pages/guide/building.html
@@ -189,22 +189,22 @@ Code size (.text section, compiled with -Os)
| Target | .text (bytes) |
-| GCC ARM32 Thumb | 4,530 |
-| GCC RISC-V (rv64) | 4,830 |
-| GCC RISC-V (rv32) | 5,068 |
-| GCC Xtensa LX106 (ESP8266) | 5,548 |
-| GCC ARM32 | 5,820 |
-| GCC m68k | 5,626 |
-| GCC x86-64 | 6,130 |
-| Clang x86-64 | 6,728 |
-| GCC AArch64 (ARM64) | 6,396 |
-| GCC x86-32 | 7,362 |
-| GCC PowerPC | 7,940 |
-| GCC MSP430 | 9,958 |
-| TCC x86 | 10,190 |
-| GCC AVR5 (ATmega328P) | 11,912 |
-| GCC AVR ATtiny85 | 12,410 |
-| GCC 68HC11 | 17,331 |
+| GCC ARM32 Thumb | 4,278 |
+| GCC RISC-V (rv64) | 4,574 |
+| GCC RISC-V (rv32) | 4,820 |
+| GCC Xtensa LX106 (ESP8266) | 5,317 |
+| GCC m68k | 5,410 |
+| GCC ARM32 | 5,504 |
+| GCC x86-64 | 5,857 |
+| GCC AArch64 (ARM64) | 6,112 |
+| Clang x86-64 | 6,555 |
+| GCC x86-32 | 6,947 |
+| GCC PowerPC | 7,540 |
+| GCC MSP430 | 9,146 |
+| TCC x86 | 9,887 |
+| GCC AVR5 (ATmega328P) | 10,806 |
+| GCC AVR ATtiny85 | 11,382 |
+| GCC 68HC11 | 16,392 |
From c23b31019833a965ab3fb845d84a891282ae908f Mon Sep 17 00:00:00 2001
From: deftio
Date: Sat, 25 Apr 2026 00:42:58 -0700
Subject: [PATCH 5/6] update badges and version strings for 2.0.6
---
README.md | 38 +++++++++++++++++++-------------------
docs/README.md | 36 ++++++++++++++++++------------------
pages/index.html | 42 +++++++++++++++++++++---------------------
3 files changed, 58 insertions(+), 58 deletions(-)
diff --git a/README.md b/README.md
index 19e958a..b3bce93 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
[](https://opensource.org/licenses/BSD-2-Clause)
[](https://github.com/deftio/fr_math/actions/workflows/ci.yml)
-[](#building-and-testing)
+[](#building-and-testing)
[](https://deftio.github.io/fr_math/)
[](release_notes.md)
@@ -80,24 +80,24 @@ 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 |
+
+| 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
diff --git a/docs/README.md b/docs/README.md
index a678336..9401590 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -45,24 +45,24 @@ radix — Q16.16 is just the reference point for the table. See the
[TDD report](../build/test_tdd_report.md) for sweeps at radixes 8, 12,
16, and 24. Percent errors skip expected values near zero (|expected| < 0.01).
-
-| 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 (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
diff --git a/pages/index.html b/pages/index.html
index 35c81d2..9746d46 100644
--- a/pages/index.html
+++ b/pages/index.html
@@ -50,27 +50,27 @@ Measured accuracy
report for sweeps at radixes 8, 12, 16, and 24.
Percent errors skip expected values near zero (|expected| < 0.01).
-
-
-| 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 (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
From 28ea312dae28c629e6c1963c4ae44b57b8eb7eb4 Mon Sep 17 00:00:00 2001
From: deftio
Date: Sat, 25 Apr 2026 10:34:11 -0700
Subject: [PATCH 6/6] udpated docs and accuracy tests
---
makefile | 36 +++++++++++++++++++++++++++++++++++-
src/FR_math.c | 6 +++---
src/FR_math.h | 2 +-
tools/make_release.sh | 20 +++++---------------
4 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/makefile b/makefile
index 168bd57..eb8a9fc 100644
--- a/makefile
+++ b/makefile
@@ -26,7 +26,41 @@ LDFLAGS = -lm
# Source files
HEADERS = $(SRC_DIR)/FR_defs.h $(SRC_DIR)/FR_math.h $(SRC_DIR)/FR_math_2D.h
-# Default target
+# Default target — print help
+.PHONY: help
+help:
+ @echo "FR_Math — Fixed Radix Math Library"
+ @echo ""
+ @echo "Usage: make "
+ @echo ""
+ @echo "Build targets:"
+ @echo " all Build library and examples"
+ @echo " lib Build library objects only"
+ @echo " examples Build example program"
+ @echo ""
+ @echo "Test targets:"
+ @echo " test Run all tests"
+ @echo " test-basic Run basic tests"
+ @echo " test-comprehensive Run comprehensive tests"
+ @echo " test-2d Run 2D math tests"
+ @echo " test-overflow Run overflow/saturation tests"
+ @echo " test-full Run full coverage tests"
+ @echo " test-2d-complete Run 2D complete coverage tests"
+ @echo " test-tdd Run TDD characterization tests"
+ @echo ""
+ @echo "Analysis targets:"
+ @echo " accuracy Show accuracy summary table"
+ @echo " accuracy-showpeak Show accuracy with peak inputs"
+ @echo " coverage Generate coverage report (gcov)"
+ @echo " coverage-basic Basic coverage info without lcov"
+ @echo " coverage-html HTML coverage report (requires lcov)"
+ @echo " size-report Multi-architecture size report"
+ @echo " size-simple Size report for current platform"
+ @echo ""
+ @echo "Maintenance:"
+ @echo " clean Remove build artifacts"
+ @echo " cleanall Remove build artifacts and backups"
+
.PHONY: all
all: dirs lib examples
diff --git a/src/FR_math.c b/src/FR_math.c
index 7f3600e..181972e 100644
--- a/src/FR_math.c
+++ b/src/FR_math.c
@@ -266,7 +266,7 @@ s32 FR_acos(s32 input, u16 radix, u16 out_radix)
/* Work with absolute value at the caller's radix — we'll need it for
* the sqrt fast path before quantising to r15. */
- sign = (input < 0) ? 1 : 0;
+ sign = (s16)((input < 0) ? 1 : 0);
input_abs = sign ? -input : input;
/* Clamp at the caller's radix — not at r15. Near ±1.0 the r15
@@ -1206,7 +1206,7 @@ s16 fr_wave_tri_morph(u16 phase, u16 break_point)
if (phase < break_point)
{
/* rising: 0 at phase=0, 32767 at phase=break_point */
- t = ((u32)phase * 32767UL) / (u32)break_point;
+ t = (u32)(((u32)phase * 32767UL) / (u32)break_point);
}
else
{
@@ -1214,7 +1214,7 @@ s16 fr_wave_tri_morph(u16 phase, u16 break_point)
u32 span = (u32)0xffff - (u32)break_point;
if (span == 0)
return 32767;
- t = ((u32)((u32)0xffff - (u32)phase) * 32767UL) / span;
+ t = (u32)(((u32)((u32)0xffff - (u32)phase) * 32767UL) / span);
}
if (t > 32767) t = 32767;
return (s16)t;
diff --git a/src/FR_math.h b/src/FR_math.h
index 3495c8a..562a5d3 100644
--- a/src/FR_math.h
+++ b/src/FR_math.h
@@ -378,7 +378,7 @@ static inline s32 FR_div_rnd(s64 num, s32 den) {
* Derivation: rad = bam * 2π / 65536. At output radix r: bam * 2π * 2^r / 2^16
* = bam * (2π * 2^10) / 2^(26 - r) = bam * 6434 >> (26 - r).
*/
-#define FR_BAM2RAD(bam, radix) (((s32)(u16)(bam) * 6434L) >> (26 - (radix)))
+#define FR_BAM2RAD(bam, radix) ((s32)(((s32)(u16)(bam) * 6434L) >> (26 - (radix))))
/*===============================================
* Radian-native and BAM-native trig (recommended)
diff --git a/tools/make_release.sh b/tools/make_release.sh
index e1a504f..28f7647 100755
--- a/tools/make_release.sh
+++ b/tools/make_release.sh
@@ -130,23 +130,13 @@ do_validate() {
run_cmd make clean >/dev/null 2>&1
echo ""
- echo " --- Strict compile (-Wall -Wextra -Werror -Wshadow) ---"
- local strict_flags="-Isrc -Wall -Wextra -Werror -Wshadow -Os"
+ echo " --- Build library + examples (uses LIB_WARN from makefile) ---"
mkdir -p build
- if ! cc ${strict_flags} -c src/FR_math.c -o build/FR_math_strict.o 2>build/strict_c.log; then
- cat build/strict_c.log
- fail "src/FR_math.c has compiler warnings"
+ if ! make lib examples 2>build/strict_build.log; then
+ cat build/strict_build.log
+ fail "Library build failed (compiler warnings or errors)"
fi
- if ! c++ ${strict_flags} -c src/FR_math_2D.cpp -o build/FR_math_2D_strict.o 2>build/strict_cpp.log; then
- cat build/strict_cpp.log
- fail "src/FR_math_2D.cpp has compiler warnings"
- fi
- pass "Zero warnings."
-
- echo ""
- echo " --- Build library + examples ---"
- run_cmd make lib examples >/dev/null 2>&1
- pass "Library and examples built."
+ pass "Library and examples built — zero warnings."
echo ""
echo " --- Full test suite ---"