Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[![License](https://img.shields.io/badge/License-BSD%202--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause)
[![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-96%25-brightgreen.svg)](#building-and-testing)
[![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.8-blue.svg)](release_notes.md)
[![Version](https://img.shields.io/badge/version-2.0.9-blue.svg)](release_notes.md)
[![GitHub](https://img.shields.io/badge/GitHub-repo-181717.svg?logo=github)](https://github.com/deftio/fr_math)

[![PlatformIO](https://img.shields.io/badge/PlatformIO-library-teal.svg)](https://registry.platformio.org/libraries/deftio/fr_math)
Expand Down Expand Up @@ -30,30 +30,30 @@ radix — Q16.16 is just the reference point for the table.
At other radixes (3-bit, 24-bit, etc.) accuracy will differ due to the
number of fractional bits available.

<!-- ACCURACY_TABLE_START -->
| Function | Max err (%)*| Avg err (%) | Note |
|---|---:|---:|---|
| sin/cos (BAM) | 0.1526 | 0.0030 | very fast binary angle trig |
| sin/cos (deg) | 0.1526 | 0.0029 | degree input trig fns |
| sin/cos (rad) | 0.1828 | 0.0033 | radian (traditional) trig |
| tan (BAM) | 0.5823 | 0.0008 | binary angle tangent; ±maxint at poles |
| tan (deg) | 0.5311 | 0.0008 | degree input tangent; saturated at poles |
| tan (rad) | 0.0386 | 0.0001 | radian (traditional) tangent |
| asin / acos | 0.7771 | 0.0280 | reverse trig, radian output |
| atan2 | 0.2564 | 0.0237 | reverse tangent, always safe |
| atan | 0.2425 | 0.0155 | reverse tangent, accepts up to maxint |
| sqrt | 0.0000 | 0.0000 | Round-to-nearest |
| log2 | 0.0116 | 0.0016 | shift/add only for speed |
| pow2 | 0.0018 | 0.0004 | shift/add only for speed |
| ln, log10 | 0.0004 | 0.0000 | shift/add only for speed |
| exp | 0.0003 | 0.0000 | shift/add only for speed |
| exp_fast | 0.0009 | 0.0001 | Shift-only scaling |
| pow10 | 0.0005 | 0.0000 | shift/add only for speed |
| pow10_fast | 0.0022 | 0.0002 | Shift-only scaling |
| hypot (exact) | 0.0000 | 0.0000 | Uses 64-bit intermediate |
| hypot_fast8 (8-seg) | 0.0915 | 0.0320 | Shift-only, no multiply |
*Relative error; reference clamped to 1% of full-scale output.
<!-- ACCURACY_TABLE_START -->
| Function | Max err (%)*| Avg err (%) | Note |
|---|---:|---:|---|
| sin/cos (BAM) | 0.1526 | 0.0030 | very fast binary angle trig |
| sin/cos (deg) | 0.1526 | 0.0029 | degree input trig fns |
| sin/cos (rad) | 0.1828 | 0.0033 | radian (traditional) trig |
| tan (BAM) | 0.5823 | 0.0008 | binary angle tangent; ±maxint at poles |
| tan (deg) | 0.5311 | 0.0008 | degree input tangent; saturated at poles |
| tan (rad) | 0.0386 | 0.0001 | radian (traditional) tangent |
| asin / acos | 0.7771 | 0.0280 | reverse trig, radian output |
| atan2 | 0.2564 | 0.0237 | reverse tangent, always safe |
| atan | 0.2425 | 0.0155 | reverse tangent, accepts up to maxint |
| sqrt | 0.0000 | 0.0000 | Round-to-nearest |
| log2 | 0.0116 | 0.0016 | shift/add only for speed |
| pow2 | 0.0018 | 0.0004 | shift/add only for speed |
| ln, log10 | 0.0004 | 0.0000 | shift/add only for speed |
| exp | 0.0003 | 0.0000 | shift/add only for speed |
| exp_fast | 0.0009 | 0.0001 | Shift-only scaling |
| pow10 | 0.0005 | 0.0000 | shift/add only for speed |
| pow10_fast | 0.0022 | 0.0002 | Shift-only scaling |
| hypot (exact) | 0.0000 | 0.0000 | Uses 64-bit intermediate |
| hypot_fast8 (8-seg) | 0.0915 | 0.0320 | Shift-only, no multiply |

*Relative error; reference clamped to 1% of full-scale output.
<!-- ACCURACY_TABLE_END -->

### What's in the box
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.8
2.0.9
10 changes: 8 additions & 2 deletions agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ src/ Core library (this is what ships)
FR_defs.h Type aliases (s8, s16, s32, u8, u16, u32)
FR_math_2D.h/.cpp Optional C++ 2D transform class

tests/ Test suite (7 programs, run via `make test`)
tests/ Test suite (8 programs, run via `make test`)
examples/ Arduino .ino sketches + POSIX example
docs/ Markdown documentation
pages/ HTML documentation (mirrors docs/)
Expand All @@ -31,7 +31,7 @@ dev/ Development notes and planning (not shipped)

```bash
make lib # compile library objects
make test # run full test suite (99% line coverage)
make test # run full test suite (98% line coverage, 100% of reachable branches)
make examples # build example programs
make size-report # cross-compile size report (Docker)
make size-update # size report + patch doc files
Expand Down Expand Up @@ -126,3 +126,9 @@ FR_Math is published to multiple package registries:
- Don't change the `extern "C"` wrapping in `FR_math.h`
- Don't hardcode a radix inside library functions — always parameterize
- Don't add `#include` dependencies beyond `<stdint.h>`
- Don't change the Arduino `u8`/`u16` typedef block in `FR_defs.h` to use
`uint8_t`/`uint16_t` — in Arduino C++ builds it must use
`unsigned char`/`unsigned short` verbatim so it stays typedef-identical
to USBAPI.h on AVR/SAM/SAMD cores while still defining the types on
cores without USBAPI.h (ESP32, RP2040, ...). See GitHub issue #11 and
`make test-arduino-compat`.
19 changes: 16 additions & 3 deletions docs/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ See `release_management.md` for the full step-by-step reference.

## The test suite

Tests live under `tests/` and are split into seven
Tests live under `tests/` and are split into eight
binaries to keep compile times low:

| Binary | What it checks |
Expand All @@ -103,9 +103,13 @@ binaries to keep compile times low:
| `test_full` | Full-coverage dark-corner cases and round-trips. |
| `test_2d_complete` | Extended 2D: matrix composition, inverse, point transforms. |
| `test_tdd` | Characterization tests pinned to bit-exact reference values. |
| `test_arduino_compat` | Arduino build compatibility: compiles the headers as ESP32-style and AVR/USBAPI-style cores would (both include orders), plus type-size checks (issue #11). |

The suite covers **99%** of the library source.
Every public symbol is exercised at least once.
The suite covers **98%** of the library source lines and every
mathematically reachable branch. The remainder is defensive code
(clamps and guards) that no input can trigger, kept in place as
insurance against future edits. Every public symbol is exercised
at least once.

### Running a single binary

Expand Down Expand Up @@ -258,8 +262,17 @@ arduino-cli compile --fqbn arduino:avr:uno examples/arduino_smoke
arduino-cli compile --fqbn arduino:avr:uno examples/basic-math
arduino-cli compile --fqbn arduino:avr:uno examples/trig-functions
arduino-cli compile --fqbn arduino:avr:uno examples/wave-generators

# Non-AVR cores work the same way, e.g. ESP32:
arduino-cli compile --fqbn esp32:esp32:esp32 examples/arduino_smoke
```

`make test-arduino-compat` (part of `make test`) simulates the
Arduino build environments on the host — ESP32-style cores with no
USBAPI.h typedefs and AVR-style cores that predefine `u8`/`u16`,
in both include orders — so core-specific type clashes like
issue #11 are caught without any board toolchain installed.

See the [code size table](#code-size-text-section-compiled-with--os) above
for exact numbers. With linker dead-code elimination, only the
functions you call are linked.
Expand Down
30 changes: 24 additions & 6 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,26 @@ back.

## Install

FR_Math is a handful of C and C++ files. There is no package
manager integration and no install step. Either:
FR_Math has no dependencies to install — it needs nothing beyond
`<stdint.h>`, which every C99-or-newer toolchain ships. "Installing"
here only means getting the sources into the project, either from a
package registry or by copying them in directly.

### From a package manager

- **Arduino (IDE or CLI)** — open the Library Manager and search for
**FR_Math**, or run `arduino-cli lib install FR_Math`. Then
`#include <FR_math.h>` in the sketch. Works on all cores (AVR,
ESP32, RP2040, SAMD, STM32, ...).
- **PlatformIO** — add `lib_deps = deftio/fr_math` to
`platformio.ini`, or run `pio pkg install --library "deftio/fr_math"`.
- **ESP-IDF** — run `idf.py add-dependency "deftio/fr_math"` to pull
it from the ESP Component Registry.

### Vendoring the sources

FR_Math is a handful of C and C++ files, so dropping it directly into
a project works just as well. Either:

- Copy `src/FR_math.c`, `src/FR_math.h`,
`src/FR_defs.h` (and optionally
Expand All @@ -18,8 +36,8 @@ manager integration and no install step. Either:
- Add FR_Math as a git submodule and point the build system at
`src/`.

There are no external dependencies beyond `<stdint.h>`,
which every C99-or-newer toolchain ships.
Nothing else needs to be added to the build — no libraries to link
(not even `libm`), no configuration step.

```bash
git clone https://github.com/deftio/fr_math.git
Expand All @@ -29,7 +47,7 @@ cd fr_math

`build.sh` wipes `build/`, rebuilds the
library, examples, and tests, and runs the full test suite. On success
the output shows all tests passing (99% line coverage).
the output shows all tests passing (98% line coverage, 100% of reachable branches).

## A first program

Expand Down Expand Up @@ -275,7 +293,7 @@ make coverage # coverage report (requires gcov)
```

Run `make test` for a full pass. With `make coverage`, line coverage of
the library sources is about **99%**. See [Building & Testing](building.md)
the library sources is about **98%**, with every mathematically reachable branch exercised. See [Building & Testing](building.md)
for targets, cross-compilation, and CI.

## Next steps
Expand Down
11 changes: 11 additions & 0 deletions docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ 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.9 — 2026

Arduino/ESP32 build fix and a saturating-add correctness fix. No API changes.

- **Arduino `u8`/`u16` type error fixed** ([issue #11](https://github.com/deftio/fr_math/issues/11)): sketches failed to compile on ESP32 — and on any core other than AVR/SAM/SAMD — with `'u16' does not name a type`. `FR_defs.h` had skipped its typedefs in Arduino C++ builds, assuming `USBAPI.h` always provides them; it now emits them using `USBAPI.h`'s exact underlying types, which works whether or not that header is present. Thanks to [@beaka](https://github.com/beaka) for the report.
- **`FR_FixAddSat` correctness**: the overflow check relied on signed-overflow UB and was optimized away at `-Os`, so negative overflow returned a wrapped positive value instead of `FR_OVERFLOW_NEG`. Also, `FR_FixAddSat(0, 0)` wrongly returned `FR_OVERFLOW_POS`. Both fixed.
- **New `test_arduino_compat` suite**: compiles the headers as each Arduino core family does (with and without `USBAPI.h` typedefs, both include orders, C and C++), catching core-specific type clashes without a board toolchain.
- **Coverage**: 98% of library source lines, every mathematically reachable branch exercised.

---

## v2.0.8 — 2026

Tangent accuracy rewrite and trig rounding fix.
Expand Down
2 changes: 1 addition & 1 deletion idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "2.0.8"
version: "2.0.9"
description: "Compact fixed-point math library for embedded systems. Integer-only with caller-selectable radix. Trig, log/exp, sqrt, hypot, wave generators, ADSR, and 2D transforms. Zero dependencies."
url: "https://github.com/deftio/fr_math"
repository: "https://github.com/deftio/fr_math.git"
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "FR_Math",
"version": "2.0.8",
"version": "2.0.9",
"description": "Compact fixed-point math library for embedded systems. Integer-only with caller-selectable radix. Trig, log/exp, sqrt, hypot, wave generators, ADSR, and 2D transforms in 4KB of flash. Zero dependencies.",
"keywords": [
"fixed-point",
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=FR_Math
version=2.0.8
version=2.0.9
author=M. A. Chatterjee <deftio@deftio.com>
maintainer=M. A. Chatterjee <deftio@deftio.com>
sentence=Compact fixed-point math library for embedded systems. 4KB flash, zero dependencies, any radix.
Expand Down
11 changes: 10 additions & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@ or libraries. Pure C99, zero dependencies beyond `<stdint.h>`.
- Repository: https://github.com/deftio/fr_math
- Documentation: https://deftio.github.io/fr_math/
- License: BSD-2-Clause
- Version: 2.0.8
- Version: 2.0.9

## Installation

- Arduino: Library Manager, search "FR_Math" (or `arduino-cli lib install FR_Math`),
then `#include <FR_math.h>`. All cores supported (AVR, ESP32, RP2040, SAMD, ...).
- PlatformIO: `lib_deps = deftio/fr_math` in platformio.ini
- ESP-IDF: `idf.py add-dependency "deftio/fr_math"`
- Vendoring: copy `src/FR_math.c`, `src/FR_math.h`, `src/FR_defs.h`
(plus `src/FR_math_2D.h/.cpp` if 2D transforms are needed) into the project.

## Key concept: radix parameter

Expand Down
25 changes: 24 additions & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ help:
@echo " test-full Run full coverage tests"
@echo " test-2d-complete Run 2D complete coverage tests"
@echo " test-tdd Run TDD characterization tests"
@echo " test-arduino-compat Run Arduino environment compatibility tests"
@echo ""
@echo "Analysis targets:"
@echo " accuracy Show accuracy summary table"
Expand Down Expand Up @@ -127,7 +128,7 @@ run-examples: examples

# Build and run tests
.PHONY: test
test: dirs examples test-basic test-comprehensive test-2d test-overflow test-full test-2d-complete test-tdd
test: dirs examples test-basic test-comprehensive test-2d test-overflow test-full test-2d-complete test-tdd test-arduino-compat

.PHONY: test-tdd
test-tdd: $(BUILD_DIR)/test_tdd
Expand All @@ -140,6 +141,28 @@ $(BUILD_DIR)/test_tdd: $(TEST_DIR)/test_tdd.cpp $(SRC_DIR)/FR_math.c $(SRC_DIR)/
$(CXX) -I$(SRC_DIR) $(LIB_WARN) -Os $(TEST_FLAGS) -c $(SRC_DIR)/FR_math_2D.cpp -o $(BUILD_DIR)/test_tdd_FR_math_2D.o
$(CXX) $(CXXFLAGS) $(TEST_FLAGS) $(TEST_DIR)/test_tdd.cpp $(BUILD_DIR)/test_tdd_FR_math.o $(BUILD_DIR)/test_tdd_FR_math_2D.o $(LDFLAGS) -o $@

# Arduino environment compatibility (issue #11): compile-matrix simulating
# Arduino cores with and without USBAPI.h's u8/u16 typedefs, plus a runtime
# type-size check. ARDUINO=10813 mimics the IDE-defined version macro.
ARDUINO_SIM = -DARDUINO=10813
.PHONY: test-arduino-compat
test-arduino-compat: dirs
@echo "Running Arduino compatibility tests (issue #11)..."
@$(CXX) -x c++ $(CFLAGS) $(ARDUINO_SIM) -DARDUINO_ARCH_ESP32 -fsyntax-only $(TEST_DIR)/test_arduino_compat.c
@echo " compile C++ ESP32-style core (no USBAPI typedefs): PASS"
@$(CXX) -x c++ $(CFLAGS) $(ARDUINO_SIM) -DFR_TEST_USBAPI_FIRST -fsyntax-only $(TEST_DIR)/test_arduino_compat.c
@echo " compile C++ AVR-style core (USBAPI typedefs before): PASS"
@$(CXX) -x c++ $(CFLAGS) $(ARDUINO_SIM) -DFR_TEST_USBAPI_AFTER -fsyntax-only $(TEST_DIR)/test_arduino_compat.c
@echo " compile C++ (USBAPI typedefs after FR_math.h): PASS"
@$(CC) $(CFLAGS) $(ARDUINO_SIM) -fsyntax-only $(TEST_DIR)/test_arduino_compat.c
@echo " compile C translation unit under Arduino: PASS"
@$(CC) -I$(SRC_DIR) $(LIB_WARN) -Os $(ARDUINO_SIM) -fsyntax-only $(SRC_DIR)/FR_math.c
@$(CXX) -I$(SRC_DIR) $(LIB_WARN) -Os $(ARDUINO_SIM) -fsyntax-only $(SRC_DIR)/FR_math_2D.cpp
@echo " compile library sources under Arduino: PASS"
@$(CC) $(CFLAGS) -c $(SRC_DIR)/FR_math.c -o $(BUILD_DIR)/test_arduino_compat_FR_math.o
@$(CXX) -x c++ $(CXXFLAGS) $(ARDUINO_SIM) $(TEST_DIR)/test_arduino_compat.c -x none $(BUILD_DIR)/test_arduino_compat_FR_math.o $(LDFLAGS) -o $(BUILD_DIR)/test_arduino_compat
@./$(BUILD_DIR)/test_arduino_compat

.PHONY: test-basic
test-basic: $(BUILD_DIR)/fr_test
@echo "Running basic tests..."
Expand Down
20 changes: 17 additions & 3 deletions pages/guide/building.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ <h3><code>tools/make_release.sh</code></h3>

<h2>The test suite</h2>

<p>Tests live under <code>tests/</code> and are split into seven
<p>Tests live under <code>tests/</code> and are split into eight
binaries to keep compile times low:</p>

<table>
Expand All @@ -120,10 +120,14 @@ <h2>The test suite</h2>
<tr><td><code>test_full</code></td><td>Full-coverage dark-corner cases and round-trips.</td></tr>
<tr><td><code>test_2d_complete</code></td><td>Extended 2D: matrix composition, inverse, point transforms.</td></tr>
<tr><td><code>test_tdd</code></td><td>Characterization tests pinned to bit-exact reference values.</td></tr>
<tr><td><code>test_arduino_compat</code></td><td>Arduino build compatibility: compiles the headers as ESP32-style and AVR/USBAPI-style cores would (both include orders), plus type-size checks (issue #11).</td></tr>
</tbody>
</table>

<p>The suite covers <strong>99%</strong> of the library source.
<p>The suite covers <strong>98%</strong> of the library source lines
and every mathematically reachable branch; the remainder is defensive
code (clamps and guards) that no input can trigger, kept in place as
insurance against future edits.
Every public symbol is exercised at least once.</p>

<h3>Running a single binary</h3>
Expand Down Expand Up @@ -275,7 +279,17 @@ <h3>Example: Arduino</h3>
# Or try the focused examples:
arduino-cli compile --fqbn arduino:avr:uno examples/basic-math
arduino-cli compile --fqbn arduino:avr:uno examples/trig-functions
arduino-cli compile --fqbn arduino:avr:uno examples/wave-generators</code></pre>
arduino-cli compile --fqbn arduino:avr:uno examples/wave-generators

# Non-AVR cores work the same way, e.g. ESP32:
arduino-cli compile --fqbn esp32:esp32:esp32 examples/arduino_smoke</code></pre>

<p><code>make test-arduino-compat</code> (part of <code>make test</code>)
simulates the Arduino build environments on the host — ESP32-style
cores with no USBAPI.h typedefs and AVR-style cores that predefine
<code>u8</code>/<code>u16</code>, in both include orders — so
core-specific type clashes like issue #11 are caught without any
board toolchain installed.</p>

<p>See the code size table above for exact numbers. With linker
dead-code elimination, only the functions you call are linked.</p>
Expand Down
Loading
Loading