-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathindex.html
More file actions
275 lines (232 loc) · 14.4 KB
/
index.html
File metadata and controls
275 lines (232 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FR_Math — fixed-point math library for embedded C</title>
<meta name="description" content="FR_Math is a C language fixed-point math library for embedded systems: trig, log, exp, 2D transforms, waves, ADSR, no floating point required.">
<link rel="stylesheet" href="assets/main.css">
</head>
<body>
<header id="site-header"></header>
<main class="page-content">
<div class="wrapper">
<h1>FR_Math</h1>
<p><strong>A C language fixed-point math library for embedded systems.</strong></p>
<p>FR_Math is a compact, integer-only fixed-point math library built for
systems where floating point is too slow, too big, or unavailable. Designed for embedded targets ranging from
legacy 16 MHz 68k processors to modern Cortex-M and RISC-V cores,
it provides a full suite of math primitives — trigonometry,
logarithms, roots, transforms, and signal generators — while
remaining deterministic, portable, and small. Unlike traditional
fixed-point libraries, FR_Math lets the caller choose the binary point
per operation, trading precision and range explicitly instead of locking
into a single format.</p>
<ul>
<li>Pure C (C99/C11/C17) with an optional C++ 2D-transform wrapper.
Tested on gcc, clang, MSVC, IAR, Keil, sdcc, AVR-gcc, MSP430-gcc,
RISC-V toolchains, and Arduino.</li>
<li>Zero dependencies beyond <code><stdint.h></code>.</li>
<li>Parameterized radix: every function takes the binary point as an
argument, so you choose how many fractional bits you need per
call.</li>
<li>Deterministic, bounded error — every public symbol has a
documented worst case in the
<a href="guide/api-reference.html">API reference</a>.</li>
</ul>
<h2>Measured accuracy</h2>
<p>Errors below are measured at Q16.16 (s15.16). All functions accept any
radix — Q16.16 is just the reference point for the table.
Run <code>make test-tdd</code> to generate the TDD report
(<code>build/test_tdd_report.md</code>) with sweeps at radixes 8, 12, 16, and 24.</p>
<!-- ACCURACY_TABLE_START -->
<table>
<thead><tr><th>Function</th><th>Max err (%)*</th><th>Avg err (%)</th><th>Note</th></tr></thead>
<tbody>
<tr><td>sin/cos (BAM)</td><td>0.1526</td><td>0.0030</td><td>very fast binary angle trig</td></tr>
<tr><td>sin/cos (deg)</td><td>0.1526</td><td>0.0029</td><td>degree input trig fns</td></tr>
<tr><td>sin/cos (rad)</td><td>0.1828</td><td>0.0033</td><td>radian (traditional) trig</td></tr>
<tr><td>tan (BAM)</td><td>0.5823</td><td>0.0008</td><td>binary angle tangent; ±maxint at poles</td></tr>
<tr><td>tan (deg)</td><td>0.5311</td><td>0.0008</td><td>degree input tangent; saturated at poles</td></tr>
<tr><td>tan (rad)</td><td>0.0386</td><td>0.0001</td><td>radian (traditional) tangent</td></tr>
<tr><td>asin / acos</td><td>0.7771</td><td>0.0280</td><td>reverse trig, radian output</td></tr>
<tr><td>atan2</td><td>0.2564</td><td>0.0237</td><td>reverse tangent, always safe</td></tr>
<tr><td>atan</td><td>0.2425</td><td>0.0155</td><td>reverse tangent, accepts up to maxint</td></tr>
<tr><td>sqrt</td><td>0.0000</td><td>0.0000</td><td>Round-to-nearest</td></tr>
<tr><td>log2</td><td>0.0116</td><td>0.0016</td><td>shift/add only for speed</td></tr>
<tr><td>pow2</td><td>0.0018</td><td>0.0004</td><td>shift/add only for speed</td></tr>
<tr><td>ln, log10</td><td>0.0004</td><td>0.0000</td><td>shift/add only for speed</td></tr>
<tr><td>exp</td><td>0.0003</td><td>0.0000</td><td>shift/add only for speed</td></tr>
<tr><td>exp_fast</td><td>0.0009</td><td>0.0001</td><td>Shift-only scaling</td></tr>
<tr><td>pow10</td><td>0.0005</td><td>0.0000</td><td>shift/add only for speed</td></tr>
<tr><td>pow10_fast</td><td>0.0022</td><td>0.0002</td><td>Shift-only scaling</td></tr>
<tr><td>hypot (exact)</td><td>0.0000</td><td>0.0000</td><td>Uses 64-bit intermediate</td></tr>
<tr><td>hypot_fast8 (8-seg)</td><td>0.0915</td><td>0.0320</td><td>Shift-only, no multiply</td></tr>
</tbody>
</table>
<p><em>*Relative error; reference clamped to 1% of full-scale output.</em></p>
<!-- ACCURACY_TABLE_END -->
<h2>What’s in the box</h2>
<table>
<thead><tr><th>Area</th><th>Functions</th></tr></thead>
<tbody>
<tr><td>Arithmetic</td><td><code>FR_ADD</code>, <code>FR_SUB</code>, <code>FR_DIV</code>, <code>FR_DIV32</code>, <code>FR_MOD</code>, <code>FR_FixMuls</code>, <code>FR_FixMulSat</code>, <code>FR_CHRDX</code></td></tr>
<tr><td>Utility</td><td><code>FR_MIN</code>, <code>FR_MAX</code>, <code>FR_CLAMP</code>, <code>FR_ABS</code>, <code>FR_SGN</code></td></tr>
<tr><td>Trig (integer deg)</td><td><code>fr_sin_deg</code>, <code>fr_cos_deg</code>, <code>fr_tan_deg</code>, <code>FR_SinI</code>, <code>FR_CosI</code>, <code>FR_TanI</code></td></tr>
<tr><td>Trig (radian/BAM)</td><td><code>fr_sin</code>, <code>fr_cos</code>, <code>fr_tan</code>, <code>fr_sin_bam</code>, <code>fr_cos_bam</code>, <code>fr_tan_bam</code></td></tr>
<tr><td>Inverse trig</td><td><code>FR_atan</code>, <code>FR_atan2</code>, <code>FR_asin</code>, <code>FR_acos</code></td></tr>
<tr><td>Log / exp</td><td><code>FR_log2</code>, <code>FR_ln</code>, <code>FR_log10</code>, <code>FR_pow2</code>, <code>FR_EXP</code>, <code>FR_POW10</code>, <code>FR_EXP_FAST</code>, <code>FR_POW10_FAST</code>, <code>FR_MULK28</code></td></tr>
<tr><td>Roots</td><td><code>FR_sqrt</code>, <code>FR_hypot</code>, <code>FR_hypot_fast8</code></td></tr>
<tr><td>Wave generators</td><td><code>fr_wave_sqr</code>, <code>fr_wave_pwm</code>, <code>fr_wave_tri</code>, <code>fr_wave_saw</code>, <code>fr_wave_tri_morph</code>, <code>fr_wave_noise</code></td></tr>
<tr><td>Envelope</td><td><code>fr_adsr_init</code>, <code>fr_adsr_trigger</code>, <code>fr_adsr_release</code>, <code>fr_adsr_step</code></td></tr>
<tr><td>2D transforms</td><td><code>FR_Matrix2D_CPT</code> (mul, add, sub, det, inv, setrotate, XFormPtI, XFormPtI16)</td></tr>
<tr><td>Formatted output</td><td><code>FR_printNumD</code>, <code>FR_printNumF</code>, <code>FR_printNumH</code>, <code>FR_numstr</code></td></tr>
</tbody>
</table>
<p>Every function is covered by the
<a href="https://github.com/deftio/fr_math/blob/master/tests/test_tdd.cpp">TDD
characterization suite</a> in the repo.</p>
<h2>Lean build options</h2>
<p>Compile-time <code>#define</code> guards let you strip optional subsystems
for ROM-constrained targets. Define them before including
<code>FR_math.h</code> (or pass <code>-D</code> on the compiler command line):</p>
<table>
<thead><tr><th>Define</th><th>What it removes</th><th>Typical savings</th></tr></thead>
<tbody>
<tr><td><code>FR_LEAN</code></td><td>Degree trig, BAM tan, angle converters, <code>FR_log10</code>, <code>FR_hypot</code>, waves + ADSR</td><td>~3.7 KB</td></tr>
<tr><td><code>FR_NO_PRINT</code></td><td><code>FR_printNumF</code>, <code>FR_printNumD</code>, <code>FR_printNumH</code>, <code>FR_numstr</code></td><td>~1.3 KB</td></tr>
<tr><td><code>FR_NO_WAVES</code></td><td><code>fr_wave_*</code> (6 shapes), <code>fr_adsr_*</code> (ADSR envelope), <code>FR_HZ2BAM_INC</code></td><td>~0.6 KB</td></tr>
</tbody>
</table>
<p><code>FR_LEAN</code> keeps only radian trig (sin, cos, tan), inverse trig,
sqrt, log2, ln, exp, pow2, and arithmetic — comparable to libfixmath’s
API but at 4.7 KB text vs libfixmath’s 4.9 KB + 112 KB BSS.
With <code>FR_LEAN</code> + <code>FR_NO_PRINT</code> the library compiles to
~4.7 KB on x86-64 / clang -Os.</p>
<pre><code class="language-c">/* Example: headless sensor node — math only, no print, no audio */
#define FR_NO_PRINT
#define FR_NO_WAVES
#include "FR_math.h"</code></pre>
<p>With <code>-ffunction-sections</code> and linker <code>--gc-sections</code>,
the linker will also strip any unused functions automatically, so these guards
are most useful when you include the library as a single <code>.c</code> file
or static archive without section-level dead-code elimination.</p>
<h2>Why fixed-point?</h2>
<p>Many modern microcontrollers have an FPU and can use <code>float</code>
freely. Older and low-cost MCUs remain common. Fixed-point is often faster and
more deterministic than <code>float</code>, and it excels in situations like:</p>
<ul>
<li><strong>8- and 16-bit MCUs</strong> (AVR, MSP430, 8051, SDCC) where the
FPU does not exist and even software float is too slow or too
large.</li>
<li><strong>Hot inner loops on any CPU</strong> where a
parameterized-radix integer multiply is faster and more
deterministic than a <code>float</code>. Think DSP taps, PID
loops, coordinate transforms inside a scanline renderer.</li>
<li><strong>Bit-exact reproducibility</strong> across compilers,
architectures, and hosts — something IEEE float does
<em>not</em> give you in the general case.</li>
<li><strong>ROM-tight builds</strong> where linking
<code>libm</code> or <code>libgcc_s</code> pulls in more code
than the whole application logic.</li>
</ul>
<p>FR_Math is engineered for these use cases. It does not try to be a
generic <code>float</code> replacement.</p>
<h2>Quick taste</h2>
<pre><code class="language-c">#include "FR_math.h"
#define R 16 /* work at radix 16 (s15.16) throughout */
/* ---- Creating fixed-point values ----
*
* FR_NUM(integer, frac_digits, num_digits, radix) encodes a decimal
* literal at compile time. The fractional part is the digits AFTER
* the decimal point, and num_digits says how many digits that is.
* Think: FR_NUM(3, 14159, 5, 16) means "3.14159" at radix 16.
*/
s32 pi = FR_NUM(3, 14159, 5, R); /* 3.14159 → raw 205886 at r16 */
s32 half = FR_NUM(0, 5, 1, R); /* 0.5 → raw 32768 */
s32 neg = FR_NUM(-2, 75, 2, R); /* -2.75 → raw -180224 */
/* Or parse from a string at runtime (no floats, no strtod): */
s32 pi2 = FR_numstr("3.14159", R); /* same result as FR_NUM above */
/* Integer-to-fixed: I2FR(n, radix) just shifts left */
s32 two = I2FR(2, R); /* 2.0 → raw 131072 */
/* ---- Naming convention: macros vs functions ----
*
* 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_DIV, FR_ABS, FR_CHRDX, FR_EXP ...
*
* MixedCase FR_ names are legacy functions — they still work but
* map to the current lowercase names:
* FR_Cos → fr_cos_deg, FR_Sin → fr_sin_deg, FR_Tan → fr_tan_deg
*
* lowercase fr_ names are the current API (degree wrappers, radian
* trig, BAM trig, wave generators, ADSR envelopes):
* fr_cos_deg, fr_sin_deg, fr_tan_deg, fr_sin, fr_cos, fr_tan,
* fr_wave_tri, fr_adsr_step ...
*
* Other MixedCase / lowercase FR_ names are functions with loops,
* tables, or multi-step algorithms:
* FR_sqrt, FR_atan2, FR_log2, FR_pow2, FR_printNumF ...
*
* Some macros wrap functions: FR_EXP(x,r) scales x then calls
* FR_pow2 — one-liner convenience, heavy lifting in the function.
*/
/* ---- Math functions ---- */
s32 c45 = fr_cos_deg(45, 0); /* cos(45°) = 0.7071 */
s32 s30 = fr_sin(FR_numstr("0.5236", R), R); /* sin(0.5236 rad) */
s32 root2 = FR_sqrt(two, R); /* sqrt(2) = 1.4142 */
s32 angle = FR_atan2(I2FR(1,R), I2FR(1,R), R); /* atan2(1,1) rad */
s32 lg = FR_log2(I2FR(1000, R), R, R); /* log2(1000) ~ 9.97 */
s32 ex = FR_EXP(I2FR(1, R), R); /* macro: scales then calls
* FR_pow2 internally */
/* ---- Printing (serial / UART / file friendly) ----
*
* FR_printNumF takes a per-character output function — works with
* putchar, Serial.write, UART_putc, or any int(*)(char). No
* sprintf, no floats, no heap. Ideal for bare-metal targets.
*/
int my_putchar(char c) { return putchar(c); } /* or your UART func */
FR_printNumF(my_putchar, pi, R, 8, 5); /* prints " 3.14159" */
FR_printNumF(my_putchar, neg, R, 8, 2); /* prints " -2.75" */
FR_printNumD(my_putchar, FR2I(lg, R), 4); /* prints " 9" (integer)*/</code></pre>
<p>See <a href="guide/getting-started.html">Getting Started</a> for a
complete walkthrough, or jump straight to the
<a href="guide/fixed-point-primer.html">Fixed-Point Primer</a> if you
want to understand <em>how</em> the radix notation works first.</p>
<h2>Comparison</h2>
<table>
<thead><tr><th>Feature</th><th>libfixmath</th><th>CMSIS-DSP</th><th>FR_Math</th></tr></thead>
<tbody>
<tr><td>Fixed format</td><td>Q16.16 only</td><td>Q31 / Q15</td><td>Any radix</td></tr>
<tr><td>Angle input</td><td>Radians (Q16.16)</td><td>Radians (float)</td><td>BAM (u16), degrees, or radians</td></tr>
<tr><td>Exact cardinal angles</td><td>No</td><td>N/A</td><td>Yes</td></tr>
<tr><td>Multiply-free option</td><td>No</td><td>No</td><td>Yes (e.g. <code>FR_EXP_FAST</code>, <code>FR_hypot_fast8</code>)</td></tr>
<tr><td>Wave generators</td><td>No</td><td>No</td><td>6 shapes + ADSR</td></tr>
<tr><td>Dependencies</td><td>None</td><td>ARM only</td><td>None</td></tr>
<tr><td>Code size (Cortex-M0, -Os)</td><td>2.4 KB</td><td>~40 KB+</td><td>3.4 KB lean / 5.7 KB full</td></tr>
</tbody>
</table>
<p><small>Sizes measured with <code>arm-none-eabi-gcc -mcpu=cortex-m0
-mthumb -Os</code>. libfixmath covers trig/sqrt/exp in Q16.16 only;
FR_Math includes log/ln/log10, wave generators, ADSR, print helpers,
and variable radix. CMSIS-DSP estimate is for the math function subset
only. See
<a href="https://github.com/deftio/fr_math/blob/master/scripts/crossbuild_sizes.sh">scripts/crossbuild_sizes.sh</a>
for the build script.</small></p>
<h2>History</h2>
<p>FR_Math has been in service since <strong>2000</strong>, originally
built for graphics transforms on 16 MHz 68k Palm Pilots (it
shipped inside Trumpetsoft’s <em>Inkstorm</em>), then ported
forward to ARM, x86, MIPS, RISC-V, and various 8/16-bit embedded
targets. The current release has a full test suite,
bit-exact numerical specification, and CI on every push.</p>
<h2>License</h2>
<p>BSD-2-Clause. Use it freely in open source or commercial projects.</p>
</div>
</main>
<footer id="site-footer"></footer>
<script src="assets/site.js"></script>
</body>
</html>