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#ifdef USE_LIBDIVIDE
8// We use pre-computed constant parameters with libdivide where possible.
9// Using predefined constants saves flash and RAM (.bss) versus calling the
10// libdivide generator functions (E.g. libdivide_s32_gen)
11// 32-bit constants generated here: https://godbolt.org/z/vP8Kfejo9
12#include <libdivide.h>
13#endif
14#include "unit_testing.h"
15
16uint8_t random1to100(void) noexcept;
17
19static constexpr uint32_t MICROS_PER_SEC = UINT32_C(1000000);
20
22static constexpr uint32_t MICROS_PER_MIN = MICROS_PER_SEC*60U;
23
26
28static constexpr uint32_t MILLI_PER_SEC = MICROS_PER_SEC/1000;
29
47#define DIV_ROUND_DOWN -1
48
50#define DIV_ROUND_UP 1
51
57#define DIV_ROUND_NEAREST 0
58
60#define DIV_ROUND_BEHAVIOR DIV_ROUND_NEAREST
61// (Unit tests expect DIV_ROUND_NEAREST behavior)
62
70#define DIV_ROUND_CORRECT(d, t) ((t)(((d)>>1U)+(t)DIV_ROUND_BEHAVIOR))
72
87#define DIV_ROUND_CLOSEST(n, d, t) ( \
88 (((n) < (t)(0)) ^ ((d) < (t)(0))) ? \
89 ((t)((n) - DIV_ROUND_CORRECT(d, t))/(t)(d)) : \
90 ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d)))
91
104#define UDIV_ROUND_CLOSEST(n, d, t) ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d))
105
113template <uint16_t divisor>
115 // This is a compile time version of UDIV_ROUND_CLOSEST
116 //
117 // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
118 // assuming d is a constant.
120}
121
123#define UDIV_ROUND_UP(n, d, t) ((t)((n) + (t)((d)+1U))/(t)(d))
124
126
128#define IS_INTEGER(d) ((d) == (int32_t)(d))
129
139static inline uint16_t div100(uint16_t n) {
140 // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
141 // (unlike the signed integer overload, where __divmodhi4 is still called
142 // see https://godbolt.org/z/c5bs5noT1)
143#ifdef USE_LIBDIVIDE
144 constexpr libdivide::libdivide_u16_t libdiv_u16_100 = { .magic = 18351, .more = 70 };
145 // LCOV_EXCL_BR_START
146 return libdivide::libdivide_u16_do_raw(n + DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), libdiv_u16_100.magic, libdiv_u16_100.more);
147 // LCOV_EXCL_BR_STOP
148#else
149 return UDIV_ROUND_CLOSEST(n, UINT16_C(100), uint16_t);
150#endif
151}
152
153static inline int16_t div100(int16_t n) {
154#ifdef USE_LIBDIVIDE
155 // Try faster unsigned path first
156 if (n>0) {
157 return div100((uint16_t)n);
158 }
159 // Negative values here, so adjust pre-division to get same
160 // behavior as roundf(float)
161 constexpr libdivide::libdivide_s16_t libdiv_s16_100 = { .magic = 20972, .more = 5 };
162 // LCOV_EXCL_BR_START
163 return libdivide::libdivide_s16_do_raw(n - DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), libdiv_s16_100.magic, libdiv_s16_100.more);
164 // LCOV_EXCL_BR_STOP
165#else
166 return DIV_ROUND_CLOSEST(n, UINT16_C(100), int16_t);
167#endif
168}
169
170static inline uint32_t div100(uint32_t n) {
171#ifdef USE_LIBDIVIDE
172 if (n<=(uint32_t)UINT16_MAX) {
173 return div100((uint16_t)n);
174 }
175 constexpr libdivide::libdivide_u32_t libdiv_u32_100 = { .magic = 2748779070, .more = 6 };
176 // LCOV_EXCL_BR_START
177 return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(100), uint32_t), libdiv_u32_100.magic, libdiv_u32_100.more);
178 // LCOV_EXCL_BR_STOP
179#else
180 return UDIV_ROUND_CLOSEST(n, UINT32_C(100), uint32_t);
181#endif
182}
183
184static inline int32_t div100(int32_t n) {
185#ifdef USE_LIBDIVIDE
187 return div100((int16_t)n);
188 }
189 constexpr libdivide::libdivide_s32_t libdiv_s32_100 = { .magic = 1374389535, .more = 5 };
190 // LCOV_EXCL_BR_START
191 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);
192 // LCOV_EXCL_BR_STOP
193#else
194 return DIV_ROUND_CLOSEST(n, INT32_C(100), int32_t);
195#endif
196}
198
205static inline uint32_t div360(uint32_t n) {
206#ifdef USE_LIBDIVIDE
207 constexpr libdivide::libdivide_u32_t libdiv_u32_360 = { .magic = 1813430637, .more = 72 };
208 // LCOV_EXCL_BR_START
209 return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(360), uint32_t), libdiv_u32_360.magic, libdiv_u32_360.more);
210 // LCOV_EXCL_BR_STOP
211#else
213#endif
214}
215
228template <uint8_t b>
230 constexpr uint8_t CORRECTION_SHIFT = b-1U; // cppcheck-suppress misra-c2012-10.4
231 constexpr uint32_t CORRECTION = 1UL<<CORRECTION_SHIFT;
232 return rshift<b>((uint32_t)(a+CORRECTION));
233}
234
236
241template <uint8_t bitsPrecision>
245}
246
248
269 // To keep the percentage within 16-bits (for performance), we have to scale the precision based on the percentage.
270 // I.e. the larger the percentage, the smaller the precision has to be (and vice-versa).
271 //
272 // We could use __builtin_clz() and use the leading zero count as the precision, but that is slow:
273 // * AVR doesn't have a clz ASM instruction, so __builtin_clz() is implemented in software
274 // * It would require removing some compile time optimizations
275 #define TEST_AND_APPLY(precision) \
276 if (percent<(UINT16_C(1)<<(UINT16_C(16)-(precision)))) { \
277 return _percentageApprox<(precision)>(percent, value); \
278 }
279
280 TEST_AND_APPLY(9) // Percent<128
281 TEST_AND_APPLY(8) // Percent<256
282 TEST_AND_APPLY(7) // Percent<512
283 TEST_AND_APPLY(6) // Percent<1024
284
285 #undef TEST_AND_APPLY
286
287 // Percent<2048
289}
290
295 if (percent<(UINT8_C(1)<<UINT8_C(7))) {
297 }
299}
300
306static constexpr uint8_t ONE_HUNDRED_PCT = 100U;
307
318
319
329#ifdef USE_LIBDIVIDE
330 constexpr libdivide::libdivide_u32_t libdiv_u32_200 = { .magic = 2748779070, .more = 7 };
331 // LCOV_EXCL_BR_START
332 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);
333 // LCOV_EXCL_BR_STOP
334#else
336#endif
337}
338
349{
350 if (value<min) { return value + nudgeAmount; }
351 if (value>max) { return value - nudgeAmount; }
352 return value;
353}
354
361template <typename TDividend, typename TDivisor>
365
377// LCOV_EXCL_START
378template<class T>
379TESTABLE_STATIC_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi){
380 return v<lo ? lo : hi<v ? hi : v;
381}
382// LCOV_EXCL_STOP
383
385
386template <typename T, typename TPrime>
387static inline T LOW_PASS_FILTER_8BIT(T input, uint8_t alpha, T prior) {
388 // Intermediate steps are for MISRA compliance
389 // Equivalent to: (input * (256 - alpha) + (prior * alpha)) >> 8
390 static constexpr uint16_t ALPHA_MAX_SHIFT = 8U;
391 static constexpr uint16_t ALPHA_MAX = 2U << (ALPHA_MAX_SHIFT-1U);
395 return (T)(preshift >> (TPrime)ALPHA_MAX_SHIFT);
396}
397
399
414
419
431static inline uint8_t scale(const uint8_t from, const uint8_t fromRange, const uint8_t toRange) {
432 // Using uint16_t to avoid overflow when calculating the result
433 return fromRange==0U ? 0U : (((uint16_t)from * (uint16_t)toRange) / (uint16_t)fromRange);
434}
435
449static 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) {
450 // Stick to unsigned math for performance, so need to check for output range inversion
451 if (toLow>toHigh) {
453 } else {
455 }
456}
457
458#endif
#define DIV_ROUND_CORRECT(d, t)
Computes the denominator correction for rounding division based on our rounding behavior.
Definition maths.h:70
TESTABLE_STATIC_CONSTEXPR uint16_t div_round_closest_u16(uint16_t n)
Rounded unsigned integer division optimized for compile time constants.
Definition maths.h:114
#define DIV_ROUND_CLOSEST(n, d, t)
Rounded integer division.
Definition maths.h:87
#define UDIV_ROUND_CLOSEST(n, d, t)
Rounded unsigned integer division.
Definition maths.h:104
static TIntegral readSerialIntegralTimeout(void)
Reads an integral type, timing out if necessary.
Definition comms.cpp:175
static uint8_t a
Definition maths.cpp:7
uint8_t random1to100(void) noexcept
Definition maths.cpp:8
static constexpr uint32_t MICROS_PER_MIN
Self-explanatory.
Definition maths.h:22
static constexpr uint32_t MICROS_PER_SEC
Self-explanatory.
Definition maths.h:19
#define TEST_AND_APPLY(precision)
static constexpr uint32_t MILLI_PER_SEC
Self-explanatory.
Definition maths.h:28
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:411
TESTABLE_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:362
static constexpr uint32_t MICROS_PER_HOUR
Self-explanatory.
Definition maths.h:25
static uint32_t percentage(uint16_t percent, uint32_t value)
Integer based percentage calculation.
Definition maths.h:314
static uint32_t div360(uint32_t n)
Optimised integer division by 360.
Definition maths.h:205
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:431
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:449
static uint32_t percentageApprox(uint16_t percent, uint32_t value)
Integer based percentage calculation: faster, but less accurate, than percentage()
Definition maths.h:268
static constexpr uint8_t ONE_HUNDRED_PCT
This is only here to eliminate magic numbers.
Definition maths.h:306
static uint32_t rshift_round(uint32_t a)
Rounded arithmetic right shift.
Definition maths.h:229
TESTABLE_STATIC_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:379
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:348
static uint16_t halfPercentage(uint8_t percent, uint16_t value)
Integer based half-percentage calculation.
Definition maths.h:327
static uint16_t div100(uint16_t n)
Performance optimised integer division by 100. I.e. same as n/100.
Definition maths.h:139
Unit testability support.
#define TESTABLE_STATIC_CONSTEXPR
Mark a function with constexpr, unless a unit test is in progress - then the entity is inlined.
Definition unit_testing.h:56