Skip to content

Latest commit

 

History

History
532 lines (434 loc) · 23.3 KB

File metadata and controls

532 lines (434 loc) · 23.3 KB

FR_Math Release Notes

Version 2.0.8 (2026)

Tangent accuracy rewrite and trig rounding fix.

BAM-native tangent table

  • New fr_tan_bam(u16 bam) function with a dedicated 65-entry octant lookup table (gFR_TAN_TAB_O in FR_trig_table.h, 130 bytes ROM). First octant uses direct table + lerp; second octant uses the reciprocal identity tan(x) = 1/tan(90-x) with one 32-bit division. No 64-bit intermediates anywhere in the tan path.
  • FR_TanI, FR_Tan, fr_tan are now thin wrappers over fr_tan_bam. The old sin/cos division implementation is removed.
  • Peak error: 0.17% (BAM), 0.60% (deg r7), 0.17% (rad r16).

Round-to-nearest fix for radian/degree wrappers

  • fr_cos, fr_sin, fr_tan, FR_Cos, FR_Sin, FR_Tan now add 0.5 LSB (1 << (radix-1)) before the >> radix shift when converting from radians/degrees to BAM. This rounds to the nearest BAM value instead of truncating, eliminating a systematic 1-BAM rounding error that caused ~1% peak error near zero crossings.
  • Radian-path sin/cos/tan now match BAM-native accuracy (0.16-0.17% peak, was ~1.03%).

Conversion macro trimming

  • FR_DEG2BAM: 10 terms (~28 bits) reduced to 7 terms (~18 bits)
  • FR_RAD2BAM: 9 terms (~27 bits) reduced to 7 terms (~21 bits)
  • FR_DEG2RAD: 3 terms (~13 bits) extended to 5 terms (~17 bits)
  • 18 bits of precision gives 4 bits of headroom over the 14-bit effective BAM resolution of the trig tables. Verified: reverting to the old full-precision macros changes sin/cos peak error by <0.04%.

Other

  • FR_TRIG_MINVAL fixed: was -FR_TRIG_MASK (-65535), now -FR_TRIG_MAXVAL (-2147483647) to properly pair with FR_TRIG_MAXVAL for tan saturation clamping.
  • Accuracy table in all docs now shows separate BAM/deg/rad rows for sin/cos and tan, matching the TDD characterization report.
  • fr_tan_bam added to function listings across README, docs, HTML pages, and llms.txt.

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 and MIPS32 toolchains added to the Docker image
  • Size table now shows three columns: Lean, Core, and Full
  • Consolidated scripts/crossbuild_sizes.sh — single script runs Docker, builds all targets, writes CSV + markdown, and patches doc files (replaces crossbuild-docker.sh, size_report.sh, update_sizes.sh)
  • Size table sorted by architecture width (8-bit → 64-bit)

README restructure

Sections reordered: accuracy table moved above the size table to lead with the library's primary selling point. Badges cleaned up from Quikdown HTML to standard markdown syntax. Build flavor descriptions made more concise.


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.

  • Fixed tools/make_release.sh failing after squash-merge: local master diverges from origin (squash creates a new commit), so git pull --ff-only fails. Now detects divergence and resets to origin/master.
  • Fixed on-master release path: script now pushes master to origin and waits for CI before tagging (previously skipped both, causing tags to point at commits not yet on the remote).
  • Release pipeline auto-commits pipeline-generated changes (badge updates, version sync) instead of failing on a dirty working tree. Unexpected dirty files still block the release.

Version 2.0.4 (2026)

CI fix release. No functional changes to the math library.

  • Fixed release.yml coverage step failing due to stale gcov invocation
  • Removed conflicting auto-release job from ci.yml (replaced by tag-triggered release.yml)
  • Documentation updated: release_management.md, docs/building.md, and pages/guide/building.html now accurately describe the guided release pipeline

Version 2.0.3 (2026)

CI and release pipeline cleanup. No functional changes to the math library itself.

Release pipeline

  • New guided release script tools/make_release.sh (ported from xelp): validates, opens PR, waits for CI, merges, tags, waits for GitHub Release, then publishes to PlatformIO and ESP-IDF registries
  • Removed old scripts/make_release.sh (validation-only, manual merge)
  • All doc/page references updated to tools/make_release.sh
  • Cleaned up stale branches

Version 2.0.2 (2026)

Embedded library publishing support. No functional changes to the math library itself — this release adds package manager integration and improves example organization.

Library publishing

  • Added library.properties for Arduino Library Manager
  • Added library.json for PlatformIO Registry
  • Added idf_component.yml + CMakeLists.txt for ESP-IDF Component Registry
  • Added keywords.txt for Arduino IDE syntax highlighting
  • Added .github/workflows/release.yml for tag-triggered release validation

Examples reorganized

  • New focused Arduino sketches: basic-math, trig-functions, wave-generators
  • Moved FR_Math_Example1.cpp to examples/posix-example/
  • Removed symlinks from examples/arduino_smoke/ (Arduino forbids symlinks)
  • Added examples/README.md

Documentation

  • Added llms.txt — machine-readable API summary for AI coding agents
  • Added agents.md — coding agent conventions and contribution guide
  • Added dev/misc/ publish checklists for Arduino, PlatformIO, and ESP-IDF
  • Updated scripts/sync_version.sh to sync version across all new metadata files
  • Added release step 11: verify llms.txt and agents.md are current

Version 2.0.1 (2026)

Precision and accuracy release. All changes are backward-compatible with v2.0.0 except where noted.

Trig output precision

  • fr_cos_bam / fr_sin_bam now return s15.16 (was s0.15). Exact values at cardinal angles: cos(0°) = 65536, cos(90°) = 0. FR_TRIG_ONE = 65536.
  • FR_TanI / fr_tan return s15.16 (was s16.15). Saturation is now ±INT32_MAX.
  • All wrappers updated: FR_Cos, FR_Sin, FR_CosI, FR_SinI, FR_TanI, FR_Tan.

Inverse trig now returns radians

  • FR_acos, FR_asin, FR_atan gain an out_radix parameter and return radians at that radix (was degrees as s16).
  • FR_atan2(y, x, out_radix) also returns radians.
  • FR_BAM2RAD macro corrected (was off by a factor of 1024).

Rounding improvements

  • FR_FixMuls / FR_FixMulSat: add 0.5 LSB (+0x8000) before the >>16 shift. Both now round to nearest instead of truncating.
  • FR_sqrt / FR_hypot: the internal fr_isqrt64 now rounds to nearest (remainder > root → +1). Worst-case error drops from <1 LSB to ≤ 0.5 LSB.
  • FR_DIV now rounds to nearest (≤ 0.5 LSB error) instead of truncating. FR_DIV_TRUNC preserves the old truncating behaviour.

Improved exp / log accuracy

  • FR_pow2 table expanded from 17 entries (16 segments) to 65 entries (64 segments, 260 bytes). Interpolation error drops by ~16×.
  • FR_log2 table expanded from 33 entries to 65 entries (6-bit index / 24-bit interpolation). Worst-case error ≤ 4 LSB at Q16.16.
  • FR_MULK28 macro added: multiplies any fixed-point value by a radix-28 constant using a 64-bit intermediate with round-to-nearest. ~9 decimal digits of precision.
  • FR_EXP and FR_POW10 now use FR_MULK28 for the base conversion instead of shift-only macros.
  • FR_ln and FR_log10 also use FR_MULK28 internally.
  • FR_EXP_FAST and FR_POW10_FAST added as shift-only alternatives for 8-bit targets where 64-bit multiply is expensive.

New symbols

  • FR_MIN, FR_MAX, FR_CLAMP — standard min/max/clamp.
  • FR_DIV_TRUNC(x, xr, y, yr) — truncating division (the old FR_DIV behaviour).
  • FR_MOD(x, xr, y, yr) — fixed-point modulus.
  • Radix-28 constants: FR_kLOG2E_28, FR_krLOG2E_28, FR_kLOG2_10_28, FR_krLOG2_10_28.

Breaking changes from v2.0.0

Change v2.0.0 v2.0.1
sin/cos return type s16 (s0.15) s32 (s15.16)
sin/cos 1.0 value 32767 65536 (exact)
tan return format s16.15 (radix 15) s15.16 (radix 16)
tan saturation ±(32767 << 15) ±INT32_MAX
FR_acos/asin signature (input, radix) → s16 degrees (input, radix, out_radix) → s32 radians
FR_atan signature (input, radix) → s16 degrees (input, radix, out_radix) → s32 radians
FR_atan2 signature (y, x) → s16 degrees (y, x, out_radix) → s32 radians
FR_BAM2RAD off by 1024× (bug) correct
FR_DIV rounding truncates toward zero rounds to nearest (use FR_DIV_TRUNC for old behaviour)

Version 2.0.0 (2026)

This is a significant bug-fix and precision-improvement release. Several v1 functions produced wrong numerical results on every platform due to arithmetic bugs; others produced wrong results specifically on 64-bit hosts because of a typedef choice. Every changed function is covered by the TDD characterization suite in tests/test_tdd.cpp.

See dev/fr_math_precision.md for the full per-symbol reference (inputs, outputs, precision, saturation) and dev/fr_math2_impl_plan.md for the implementation plan this release executed.

64-bit safety (portability)

  • FR_defs.h: migrated typedefs to <stdint.h> (s8int8_t, s32int32_t, etc.). v1 defined s32 as signed long, which is 64 bits on LP64 platforms (Linux and macOS on x64 / ARM64), so every fixed-point computation that relied on s32 being exactly 32 bits silently produced wrong answers on desktop Linux and macOS. C99 <stdint.h> is now mandatory in v2 (every modern toolchain — gcc, clang, MSVC, IAR, Keil C51, sdcc, MSP430-gcc, AVR-gcc, RISC-V/ARM — ships it). If you are stuck on a pre-C99 compiler, FR_Math 1.0.x remains the version for you.

Numerical fixes

  • FR_FixMulSat / FR_FixMuls: v1 used a split-multiply formula with an algebraic bug that returned wrong values for certain sign combinations. v2 uses an int64_t fast path with explicit saturation.
  • FR_log2: v1 was missing the accumulator in the mantissa-table step and returned wrong values for non-power-of-2 inputs. v2 rewrites the algorithm: leading-bit-position → normalize to s1.30 → 33-entry mantissa lookup with linear interpolation.
  • FR_ln / FR_log10: inherit the FR_log2 fix automatically (they are multiplies of FR_log2 by a constant).
  • FR_pow2: v1 used C truncation toward zero to extract the integer exponent, which gave wrong answers for negative non-integer inputs (e.g. FR_pow2(-0.5) returned ~2 instead of ~0.707). v2 uses mathematical floor (toward −∞) and a 17-entry fraction table with linear interp.
  • FR_atan2: v1 was a placeholder that returned garbage. v2 is a correct octant-reduced arctan with a 33-entry table. Max error ≤ 1°. v2 also drops the vestigial radix parameter — new signature is FR_atan2(s32 y, s32 x).
  • FR_atan: v1 was wired up incorrectly. v2 implements as FR_atan2(input, 1<<radix).
  • FR_Tan: v1 used s16 loop variables that overflowed for angles near 90°. v2 uses s32.
  • FR_TanI: removed dead unreachable code (if (270 == deg)).
  • FR_printNumD: v1 invoked unary minus on INT_MIN, which is undefined behavior, and always returned 0. v2 works in unsigned magnitude and returns the real byte count (or −1 on null f).
  • FR_printNumF: same INT_MIN fix, plus v1's fraction extraction was mathematically wrong and printed bogus tail digits.
  • FR_printNumH: v1 shifted a signed negative int right. v2 casts to unsigned first.

Macro fixes

  • FR_DEG2RAD and FR_RAD2DEG: v1 had the macro bodies swapped relative to their names, so FR_DEG2RAD(x) actually multiplied by 57.3 and FR_RAD2DEG(x) multiplied by 0.017. v2 swaps them back. FR_DEG2RAD also had missing parens around x in a subexpression.
  • FR_NUM: v1 signature was FR_NUM(i, f, r) and the body ignored the f argument entirely. v2 adds an explicit d digit-count argument — new signature is FR_NUM(i, f, d, r). Any caller of the v1 form must be updated.

New functionality

  • Radian-native trig (fr_cos, fr_sin, fr_tan, fr_cos_bam, fr_sin_bam, fr_cos_deg, fr_sin_deg): takes radians (or BAM, or integer degrees) directly rather than forcing everything through integer degrees. Uses a new 129-entry s0.15 quadrant cosine table in src/FR_trig_table.h with linear interpolation and round-to-nearest. Max error ≤ 1 LSB of s0.15 (~3e−5). Mean error ~0.
  • BAM (Binary Angular Measure) macros: FR_DEG2BAM, FR_BAM2DEG, FR_RAD2BAM, FR_BAM2RAD. BAM is the natural integer representation for trig: 16 bits per full circle, top 2 bits select the quadrant, next 7 bits index the table, bottom 7 bits drive interpolation.
  • Table size is a compile-time knob: -DFR_TRIG_TABLE_BITS=8 gives a 257-entry table for halved worst-case error (default is 7 / 129 entries).
  • Square root and hypot (FR_sqrt, FR_hypot): radix-aware fixed point square root and 2D vector magnitude. FR_sqrt uses a digit-by-digit (shift-and-subtract) integer isqrt on int64_t. Negative input returns FR_DOMAIN_ERROR (INT32_MIN). FR_hypot computes sqrt(x^2 + y^2) with no intermediate overflow up to the full s32 range. Bit-exact for perfect squares; max error ~1 LSB at the requested radix.
  • Fast approximate magnitude (FR_hypot_fast8): shift-only piecewise-linear approximation of sqrt(x^2 + y^2) — no multiply, no divide, no 64-bit math, no ROM table, no iteration. FR_hypot_fast8 uses 8 segments (~0.14% peak error). Based on the method of US Patent 6,567,777 B1 (Chatterjee, public domain). No radix parameter needed — the algorithm is scale-invariant.
  • Wave function family for embedded audio / LFOs / control signals. All take a u16 BAM phase and return s0.15 (s16, ±32767):
    • fr_wave_sqr(phase) — symmetric square wave.
    • fr_wave_pwm(phase, duty) — variable-duty pulse wave; duty is a u16 threshold in BAM units.
    • fr_wave_tri(phase) — symmetric triangle, peaks clamped to ±32767. Max error vs ideal triangle ~3e−5 (1 LSB s0.15).
    • fr_wave_saw(phase) — sawtooth, (s16)(phase - 0x8000) with boundary clamp.
    • fr_wave_tri_morph(phase, break_point) — variable-symmetry triangle that morphs into a sawtooth as break_point approaches 0 or 0xffff. Returns unipolar [0, 32767]. Uses one division per sample.
    • fr_wave_noise(state) — 32-bit Galois LFSR (poly 0xD0000001, period 2^32 − 1) returning a full-range s16. Caller owns the u32 state; seed with any non-zero value.
  • FR_HZ2BAM_INC(hz, sample_rate): phase increment helper. Computes hz * 65536 / sample_rate so a u16 phase accumulator driven by this increment produces the requested frequency. Example: FR_HZ2BAM_INC(440, 48000) = 600 (≈ 439.45 Hz).
  • ADSR envelope generator (fr_adsr_t, fr_adsr_init, fr_adsr_trigger, fr_adsr_release, fr_adsr_step): linear-segment attack/decay/sustain/release envelope. Internal levels are stored as s1.30 so very long durations (e.g. 48000-sample attack at 48 kHz) still get a non-zero per-sample increment; fr_adsr_step returns s0.15 for direct multiplication into a wave sample. State machine exposes constants FR_ADSR_IDLE / _ATTACK / _DECAY / _SUSTAIN / _RELEASE.

New documentation

  • dev/fr_math_precision.md — comprehensive per-symbol precision reference. Every public macro, constant, and function is documented with inputs, output format, worst-case error, saturation behavior, and side-effect notes.
  • CONTRIBUTING.md — PR expectations, test discipline, portability rules, commit message format.
  • tools/interp_analysis.html — interactive Chart.js analysis of trig interpolation methods. Compares nearest-neighbor, linear truncated, linear rounded, cosine interp, smoothstep, Hermite cubic, and Catmull-Rom on the same 129-entry table, over θ ∈ [−45°, +45°]. Includes pan/zoom on a second chart showing the actual interpolated values vs Math.cos reference. Use this to verify interpolation claims and to pick a default for the library.
  • scripts/clean_build.sh — one-shot clean of build/ and coverage/ directories. Handy when switching branches or debugging stale object files.

Breaking changes

  • FR_NUM signature: FR_NUM(i, f, r)FR_NUM(i, f, d, r). Any code that called the 3-argument form will fail to compile. The old form was broken (it ignored f entirely), so any caller was already getting wrong results.
  • FR_atan2 signature: FR_atan2(y, x, radix)FR_atan2(y, x). The radix parameter was vestigial (ignored by the v1 placeholder and unnecessary in v2).
  • FR_RESULT and FR_E_* codes removed: v1's HRESULT-style return codes are gone. The matrix inv() methods now return bool (true on success, false if singular). add(), sub(), and setrotate() return void. Math functions that can hit a domain error return the named sentinel FR_DOMAIN_ERROR; saturating arithmetic returns FR_OVERFLOW_POS / FR_OVERFLOW_NEG.
  • FR_SQUARE and FR_FIXMUL32u removed: these s16.16-only macros were narrow specializations of FR_FixMuls/FR_FixMulSat. Use the generic versions, which now have an int64_t fast path and work at any radix.
  • FR_NO_INT64 and FR_NO_STDINT build flags removed: every C99-or-newer toolchain on every architecture (including 8-bit targets like sdcc, AVR-gcc, MSP430-gcc) provides <stdint.h> and 64-bit integer arithmetic, so the conditional fallbacks were carrying their weight in maintenance and ROM for no benefit.
  • Wider intermediate types: if you had code that poked at s32 as if it were long (e.g. printing with %ld), that will now warn. Use %d with s32 / int32_t, or cast.

Version 1.0.3 (2025)

Test Coverage Improvements

  • Increased overall test coverage from 4% to 72%
    • FR_math.c: 60% coverage
    • FR_math_2D.cpp: 98% coverage
  • Added comprehensive test suite with multiple test files:
    • test_comprehensive.c - Comprehensive tests for core math functions
    • test_overflow_saturation.c - Edge case and overflow behavior tests
    • test_full_coverage.c - Tests for previously uncovered functions
    • test_2d_complete.cpp - Complete 2D transformation matrix tests
  • Fixed test failures in 2D transformation tests (XFormPtI now correctly expects integer inputs)

Bug Fixes

  • Fixed FR_atan function - Was incorrectly calling FR_atan2 with wrong arguments, now properly calls FR_atanI
  • Fixed constant declarations - Changed FR_PI, FR_2PI, FR_E from non-const to proper const declarations
  • Fixed XFormPtI test assumptions - Tests were incorrectly passing fixed-point values instead of integers

Build System Enhancements

  • Added comprehensive Makefile targets:
    • make lib - Build static library
    • make test - Run all tests
    • make coverage - Generate coverage reports with lcov
    • make examples - Build example programs
    • make clean - Clean build artifacts
  • Added GitHub Actions CI/CD:
    • Multi-platform testing (Ubuntu, macOS)
    • Multi-compiler support (gcc, clang)
    • Cross-compilation testing (ARM, RISC-V)
    • 32-bit compatibility testing
    • Automated coverage reporting to Codecov
    • Overflow and saturation testing with sanitizers

Documentation Improvements

  • Cleaned up README.md:
    • Fixed grammar and spelling throughout
    • Added "Building and Testing" section with clear instructions
    • Improved formatting with consistent markdown usage
    • Added supported platforms list
    • Better code examples with syntax highlighting
    • Clearer mathematical notation and explanations
  • Added CLAUDE.md - Assistant-friendly documentation with:
    • Project structure overview
    • Key concepts and math notation explained
    • Testing and linting commands
    • Common pitfalls and tips
  • Added inline documentation for test files explaining coverage goals

Code Quality

  • Removed unused/unimplemented functions:
    • Removed FR_atan declaration (was declared but never implemented)
    • Cleaned up test code to remove references to non-existent functions
  • Fixed compiler warnings:
    • Resolved deprecated C++ warnings
    • Fixed format string warnings in tests
    • Addressed unused variable warnings
  • Improved code organization:
    • Better separation of test types
    • Clearer test naming conventions
    • More maintainable test structure

Platform Support

  • Verified compilation and testing on:
    • x86/x64 (Linux, macOS)
    • ARM (32-bit and 64-bit)
    • RISC-V
    • 32-bit x86 targets
  • Added cross-compilation support in CI

Version Updates

  • Updated all source file headers to version 1.0.3
  • Added version section to README.md

Previous Versions

Version 1.02

  • Initial public release
  • Basic fixed-point math operations
  • 2D transformation matrices
  • Core trigonometric functions

Version 1.01

  • Internal development version
  • Cleaned up naming conventions
  • Initial test framework

Note: FR_Math has been in development since 2000, originally used in embedded systems for Palm Pilots and later ARM cores. This is the first version with comprehensive testing and CI/CD integration.