Speeduino
Loading...
Searching...
No Matches
maths.h
Go to the documentation of this file.
1#ifndef MATH_H
2#define MATH_H
3
4#include <stdint.h>
5#include <avr-fast-shift.h>
6#include <avr-fast-div.h>
7
8#ifdef USE_LIBDIVIDE
9// We use pre-computed constant parameters with libdivide where possible.
10// Using predefined constants saves flash and RAM (.bss) versus calling the
11// libdivide generator functions (E.g. libdivide_s32_gen)
12// 32-bit constants generated here: https://godbolt.org/z/vP8Kfejo9
13#include <libdivide.h>
14#endif
15
17
35#define DIV_ROUND_DOWN -1
36
38#define DIV_ROUND_UP 1
39
45#define DIV_ROUND_NEAREST 0
46
48#define DIV_ROUND_BEHAVIOR DIV_ROUND_NEAREST
49// (Unit tests expect DIV_ROUND_NEAREST behavior)
50
58#define DIV_ROUND_CORRECT(d, t) ((t)(((d)>>1U)+(t)DIV_ROUND_BEHAVIOR))
60
75#define DIV_ROUND_CLOSEST(n, d, t) ( \
76 (((n) < (t)(0)) ^ ((d) < (t)(0))) ? \
77 ((t)((n) - DIV_ROUND_CORRECT(d, t))/(t)(d)) : \
78 ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d)))
79
92#define UDIV_ROUND_CLOSEST(n, d, t) ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d))
93
101template <uint16_t divisor>
103 // This is a compile time version of UDIV_ROUND_CLOSEST
104 //
105 // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
106 // assuming d is a constant.
108}
110
112#define IS_INTEGER(d) ((d) == (int32_t)(d))
113
123static inline uint16_t div100(uint16_t n) {
124 // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
125 // (unlike the signed integer overload, where __divmodhi4 is still called
126 // see https://godbolt.org/z/c5bs5noT1)
127#ifdef USE_LIBDIVIDE
128 constexpr libdivide::libdivide_u16_t libdiv_u16_100 = { .magic = 18351, .more = 70 };
129 return libdivide::libdivide_u16_do_raw(n + DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), libdiv_u16_100.magic, libdiv_u16_100.more);
130#else
131 return UDIV_ROUND_CLOSEST(n, UINT16_C(100), uint16_t);
132#endif
133}
134
135static inline int16_t div100(int16_t n) {
136#ifdef USE_LIBDIVIDE
137 // Try faster unsigned path first
138 if (n>0) {
139 return div100((uint16_t)n);
140 }
141 // Negative values here, so adjust pre-division to get same
142 // behavior as roundf(float)
143 constexpr libdivide::libdivide_s16_t libdiv_s16_100 = { .magic = 20972, .more = 5 };
144 return libdivide::libdivide_s16_do_raw(n - DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), libdiv_s16_100.magic, libdiv_s16_100.more);
145#else
146 return DIV_ROUND_CLOSEST(n, UINT16_C(100), int16_t);
147#endif
148}
149
150static inline uint32_t div100(uint32_t n) {
151#ifdef USE_LIBDIVIDE
152 if (n<=(uint32_t)UINT16_MAX) {
153 return div100((uint16_t)n);
154 }
155 constexpr libdivide::libdivide_u32_t libdiv_u32_100 = { .magic = 2748779070, .more = 6 };
156 return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(100), uint32_t), libdiv_u32_100.magic, libdiv_u32_100.more);
157#else
158 return UDIV_ROUND_CLOSEST(n, UINT32_C(100), uint32_t);
159#endif
160}
161
162static inline int32_t div100(int32_t n) {
163#ifdef USE_LIBDIVIDE
165 return div100((int16_t)n);
166 }
167 constexpr libdivide::libdivide_s32_t libdiv_s32_100 = { .magic = 1374389535, .more = 5 };
168 return libdivide::libdivide_s32_do_raw(n + (DIV_ROUND_CORRECT(UINT16_C(100), uint32_t) * (n<0 ? -1 : 1)), libdiv_s32_100.magic, libdiv_s32_100.more);
169#else
170 return DIV_ROUND_CLOSEST(n, INT32_C(100), int32_t);
171#endif
172}
174
181static inline uint32_t div360(uint32_t n) {
182#ifdef USE_LIBDIVIDE
183 constexpr libdivide::libdivide_u32_t libdiv_u32_360 = { .magic = 1813430637, .more = 72 };
184 return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(360), uint32_t), libdiv_u32_360.magic, libdiv_u32_360.more);
185#else
187#endif
188}
189
202template <uint8_t b>
204 return rshift<b>(a+(1UL<<((uint32_t)b-1UL)));
205}
206
208
213template <uint8_t bitsPrecision>
217}
218
220
241 // To keep the percentage within 16-bits (for performance), we have to scale the precision based on the percentage.
242 // I.e. the larger the percentage, the smaller the precision has to be (and vice-versa).
243 //
244 // We could use __builtin_clz() and use the leading zero count as the precision, but that is slow:
245 // * AVR doesn't have a clz ASM instruction, so __builtin_clz() is implemented in software
246 // * It would require removing some compile time optimizations
247 #define TEST_AND_APPLY(precision) \
248 if (percent<(UINT16_C(1)<<(UINT16_C(16)-(precision)))) { \
249 return _percentageApprox<(precision)>(percent, value); \
250 }
251
252 TEST_AND_APPLY(9) // Percent<128
253 TEST_AND_APPLY(8) // Percent<256
254 TEST_AND_APPLY(7) // Percent<512
255 TEST_AND_APPLY(6) // Percent<1024
256
257 #undef TEST_AND_APPLY
258
259 // Percent<2048
261}
262
272
283
284
294#ifdef USE_LIBDIVIDE
295 constexpr libdivide::libdivide_u32_t libdiv_u32_200 = { .magic = 2748779070, .more = 7 };
296 return (uint16_t)libdivide::libdivide_u32_do_raw(x200 + DIV_ROUND_CORRECT(UINT32_C(200), uint32_t), libdiv_u32_200.magic, libdiv_u32_200.more);
297#else
299#endif
300}
301
312{
313 if (value<min) { return value + nudgeAmount; }
314 if (value>max) { return value - nudgeAmount; }
315 return value;
316}
317
324template <typename TDividend, typename TDivisor>
328
340template<class T>
341constexpr const T& clamp(const T& v, const T& lo, const T& hi){
342 return v<lo ? lo : hi<v ? hi : v;
343}
344
346
347template <typename T, typename TPrime>
348static inline T LOW_PASS_FILTER_8BIT(T input, uint8_t alpha, T prior) {
349 // Intermediate steps are for MISRA compliance
350 // Equivalent to: (input * (256 - alpha) + (prior * alpha)) >> 8
351 static constexpr uint16_t ALPHA_MAX_SHIFT = 8U;
352 static constexpr uint16_t ALPHA_MAX = 2U << (ALPHA_MAX_SHIFT-1U);
356 return (T)(preshift >> (TPrime)ALPHA_MAX_SHIFT);
357}
358
360
375
380
392static inline uint8_t scale(const uint8_t from, const uint8_t fromRange, const uint8_t toRange) {
393 // Using uint16_t to avoid overflow when calculating the result
394 return fromRange==0U ? 0U : (((uint16_t)from * (uint16_t)toRange) / (uint16_t)fromRange);
395}
396
410static inline uint8_t fast_map(const uint8_t from, const uint8_t fromLow, const uint8_t fromHigh, const uint8_t toLow, const uint8_t toHigh) {
411 // Stick to unsigned math for performance, so need to check for output range inversion
412 if (toLow>toHigh) {
414 } else {
416 }
417}
418
419#endif
#define DIV_ROUND_CORRECT(d, t)
Computes the denominator correction for rounding division based on our rounding behavior.
Definition maths.h:58
static constexpr uint16_t div_round_closest_u16(uint16_t n)
Rounded unsigned integer division optimized for compile time constants.
Definition maths.h:102
#define DIV_ROUND_CLOSEST(n, d, t)
Rounded integer division.
Definition maths.h:75
#define UDIV_ROUND_CLOSEST(n, d, t)
Rounded unsigned integer division.
Definition maths.h:92
static TIntegral readSerialIntegralTimeout(void)
Reads an integral type, timing out if necessary.
Definition comms.cpp:173
static uint8_t a
Definition maths.cpp:7
uint8_t random1to100(void)
Definition maths.cpp:8
#define TEST_AND_APPLY(precision)
static uint16_t LOW_PASS_FILTER(uint16_t input, uint8_t alpha, uint16_t prior)
Simple low pass IIR filter 16-bit values.
Definition maths.h:372
static uint32_t percentage(uint16_t percent, uint32_t value)
Integer based percentage calculation.
Definition maths.h:279
static uint32_t div360(uint32_t n)
Optimised integer division by 360.
Definition maths.h:181
static uint8_t scale(const uint8_t from, const uint8_t fromRange, const uint8_t toRange)
Scale a value from one range to another.
Definition maths.h:392
static uint8_t fast_map(const uint8_t from, const uint8_t fromLow, const uint8_t fromHigh, const uint8_t toLow, const uint8_t toHigh)
Specialist version of map(long, long, long, long, long) for performance.
Definition maths.h:410
static uint32_t percentageApprox(uint16_t percent, uint32_t value)
Integer based percentage calculation: faster, but less accurate, than percentage()
Definition maths.h:240
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
clamps a given value between the minimum and maximum thresholds.
Definition maths.h:341
static uint32_t rshift_round(uint32_t a)
Rounded arithmetic right shift.
Definition maths.h:203
static int16_t nudge(int16_t min, int16_t max, int16_t value, int16_t nudgeAmount)
Make one pass at correcting the value into the range [min, max)
Definition maths.h:311
static constexpr TDividend fast_div_closest(TDividend dividend, TDivisor divisor)
Same as fast_div(), except this will round to nearest integer instead of truncating.
Definition maths.h:325
static uint16_t halfPercentage(uint8_t percent, uint16_t value)
Integer based half-percentage calculation.
Definition maths.h:292
static uint16_t div100(uint16_t n)
Performance optimised integer division by 100. I.e. same as n/100.
Definition maths.h:123