Repository navigation
FR_Math 2.0.0
FR_Math Release Notes
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>(s8→int8_t,
s32→int32_t, etc.). v1 defineds32assigned long, which is
64 bits on LP64 platforms (Linux and macOS on x64 / ARM64), so every
fixed-point computation that relied ons32being 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 anint64_tfast 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 theFR_log2fix automatically
(they are multiplies ofFR_log2by 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 vestigialradixparameter — 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 useds16loop variables that overflowed for angles
near 90°. v2 usess32.FR_TanI: removed dead unreachable code (if (270 == deg)).FR_printNumD: v1 invoked unary minus onINT_MIN, which is
undefined behavior, and always returned 0. v2 works in unsigned
magnitude and returns the real byte count (or −1 on nullf).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_DEG2RADandFR_RAD2DEG: v1 had the macro bodies swapped
relative to their names, soFR_DEG2RAD(x)actually multiplied by
57.3 andFR_RAD2DEG(x)multiplied by 0.017. v2 swaps them back.
FR_DEG2RADalso had missing parens aroundxin a subexpression.FR_NUM: v1 signature wasFR_NUM(i, f, r)and the body ignored
thefargument entirely. v2 adds an explicitddigit-count
argument — new signature isFR_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.hwith 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=8gives
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_sqrtuses a
digit-by-digit (shift-and-subtract) integer isqrt onint64_t.
Negative input returnsFR_DOMAIN_ERROR(INT32_MIN).FR_hypot
computessqrt(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_fast,FR_hypot_fast8):
shift-only piecewise-linear approximation ofsqrt(x^2 + y^2)— no
multiply, no divide, no 64-bit math, no ROM table, no iteration.
FR_hypot_fastuses 4 segments (~0.4% peak error);
FR_hypot_fast8uses 8 segments (~0.14% peak error). Based on the
method of US Patent 6,567,777 B1 (Chatterjee, public domain). No
radixparameter needed — the algorithm is scale-invariant. - Wave function family for embedded audio / LFOs / control
signals. All take au16BAM phase and return s0.15 (s16, ±32767):fr_wave_sqr(phase)— symmetric square wave.fr_wave_pwm(phase, duty)— variable-duty pulse wave;dutyis
au16threshold 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 asbreak_pointapproaches
0or0xffff. Returns unipolar [0, 32767]. Uses one division
per sample.fr_wave_noise(state)— 32-bit Galois LFSR (poly0xD0000001,
period 2^32 − 1) returning a full-range s16. Caller owns the
u32state; seed with any non-zero value.
FR_HZ2BAM_INC(hz, sample_rate): phase increment helper.
Computeshz * 65536 / sample_rateso 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_stepreturns
s0.15 for direct multiplication into a wave sample. State machine
exposes constantsFR_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 vsMath.cosreference. Use this to verify interpolation
claims and to pick a default for the library.scripts/clean_build.sh— one-shot clean ofbuild/and
coverage/directories. Handy when switching branches or debugging
stale object files.
Breaking changes
FR_NUMsignature: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 ignoredfentirely), so any caller was already
getting wrong results.FR_atan2signature:FR_atan2(y, x, radix)→FR_atan2(y, x).
The radix parameter was vestigial (ignored by the v1 placeholder and
unnecessary in v2).FR_RESULTandFR_E_*codes removed: v1's HRESULT-style return
codes are gone. The matrixinv()methods now returnbool
(trueon success,falseif singular).add(),sub(), and
setrotate()returnvoid. Math functions that can hit a domain
error return the named sentinelFR_DOMAIN_ERROR; saturating
arithmetic returnsFR_OVERFLOW_POS/FR_OVERFLOW_NEG.FR_SQUAREandFR_FIXMUL32uremoved: these s16.16-only macros
were narrow specializations ofFR_FixMuls/FR_FixMulSat. Use the
generic versions, which now have anint64_tfast path and work at
any radix.FR_NO_INT64andFR_NO_STDINTbuild 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
s32as
if it werelong(e.g. printing with%ld), that will now warn.
Use%dwiths32/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 functionstest_overflow_saturation.c- Edge case and overflow behavior teststest_full_coverage.c- Tests for previously uncovered functionstest_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 librarymake test- Run all testsmake coverage- Generate coverage reports with lcovmake examples- Build example programsmake 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.