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
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 // LCOV_EXCL_BR_START
130 return libdivide::libdivide_u16_do_raw(n + DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), libdiv_u16_100.magic, libdiv_u16_100.more);
131 // LCOV_EXCL_BR_STOP
132#else
133 return UDIV_ROUND_CLOSEST(n, UINT16_C(100), uint16_t);
134#endif
135}
136
137static inline int16_t div100(int16_t n) {
138#ifdef USE_LIBDIVIDE
139 // Try faster unsigned path first
140 if (n>0) {
141 return div100((uint16_t)n);
142 }
143 // Negative values here, so adjust pre-division to get same
144 // behavior as roundf(float)
145 constexpr libdivide::libdivide_s16_t libdiv_s16_100 = { .magic = 20972, .more = 5 };
146 // LCOV_EXCL_BR_START
147 return libdivide::libdivide_s16_do_raw(n - DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), libdiv_s16_100.magic, libdiv_s16_100.more);
148 // LCOV_EXCL_BR_STOP
149#else
150 return DIV_ROUND_CLOSEST(n, UINT16_C(100), int16_t);
151#endif
152}
153
154static inline uint32_t div100(uint32_t n) {
155#ifdef USE_LIBDIVIDE
156 if (n<=(uint32_t)UINT16_MAX) {
157 return div100((uint16_t)n);
158 }
159 constexpr libdivide::libdivide_u32_t libdiv_u32_100 = { .magic = 2748779070, .more = 6 };
160 // LCOV_EXCL_BR_START
161 return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(100), uint32_t), libdiv_u32_100.magic, libdiv_u32_100.more);
162 // LCOV_EXCL_BR_STOP
163#else
164 return UDIV_ROUND_CLOSEST(n, UINT32_C(100), uint32_t);
165#endif
166}
167
168static inline int32_t div100(int32_t n) {
169#ifdef USE_LIBDIVIDE
171 return div100((int16_t)n);
172 }
173 constexpr libdivide::libdivide_s32_t libdiv_s32_100 = { .magic = 1374389535, .more = 5 };
174 // LCOV_EXCL_BR_START
175 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);
176 // LCOV_EXCL_BR_STOP
177#else
178 return DIV_ROUND_CLOSEST(n, INT32_C(100), int32_t);
179#endif
180}
182
189static inline uint32_t div360(uint32_t n) {
190#ifdef USE_LIBDIVIDE
191 constexpr libdivide::libdivide_u32_t libdiv_u32_360 = { .magic = 1813430637, .more = 72 };
192 // LCOV_EXCL_BR_START
193 return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(360), uint32_t), libdiv_u32_360.magic, libdiv_u32_360.more);
194 // LCOV_EXCL_BR_STOP
195#else
197#endif
198}
199
212template <uint8_t b>
214 constexpr uint8_t CORRECTION_SHIFT = b-1U; // cppcheck-suppress misra-c2012-10.4
215 constexpr uint32_t CORRECTION = 1UL<<CORRECTION_SHIFT;
216 return rshift<b>((uint32_t)(a+CORRECTION));
217}
218
220
225template <uint8_t bitsPrecision>
229}
230
232
253 // To keep the percentage within 16-bits (for performance), we have to scale the precision based on the percentage.
254 // I.e. the larger the percentage, the smaller the precision has to be (and vice-versa).
255 //
256 // We could use __builtin_clz() and use the leading zero count as the precision, but that is slow:
257 // * AVR doesn't have a clz ASM instruction, so __builtin_clz() is implemented in software
258 // * It would require removing some compile time optimizations
259 #define TEST_AND_APPLY(precision) \
260 if (percent<(UINT16_C(1)<<(UINT16_C(16)-(precision)))) { \
261 return _percentageApprox<(precision)>(percent, value); \
262 }
263
264 TEST_AND_APPLY(9) // Percent<128
265 TEST_AND_APPLY(8) // Percent<256
266 TEST_AND_APPLY(7) // Percent<512
267 TEST_AND_APPLY(6) // Percent<1024
268
269 #undef TEST_AND_APPLY
270
271 // Percent<2048
273}
274
284
290static constexpr uint8_t ONE_HUNDRED_PCT = 100U;
291
302
303
313#ifdef USE_LIBDIVIDE
314 constexpr libdivide::libdivide_u32_t libdiv_u32_200 = { .magic = 2748779070, .more = 7 };
315 // LCOV_EXCL_BR_START
316 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);
317 // LCOV_EXCL_BR_STOP
318#else
320#endif
321}
322
333{
334 if (value<min) { return value + nudgeAmount; }
335 if (value>max) { return value - nudgeAmount; }
336 return value;
337}
338
345template <typename TDividend, typename TDivisor>
349
361// LCOV_EXCL_START
362template<class T>
363TESTABLE_STATIC_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi){
364 return v<lo ? lo : hi<v ? hi : v;
365}
366// LCOV_EXCL_STOP
367
369
370template <typename T, typename TPrime>
371static inline T LOW_PASS_FILTER_8BIT(T input, uint8_t alpha, T prior) {
372 // Intermediate steps are for MISRA compliance
373 // Equivalent to: (input * (256 - alpha) + (prior * alpha)) >> 8
374 static constexpr uint16_t ALPHA_MAX_SHIFT = 8U;
375 static constexpr uint16_t ALPHA_MAX = 2U << (ALPHA_MAX_SHIFT-1U);
379 return (T)(preshift >> (TPrime)ALPHA_MAX_SHIFT);
380}
381
383
398
403
415static inline uint8_t scale(const uint8_t from, const uint8_t fromRange, const uint8_t toRange) {
416 // Using uint16_t to avoid overflow when calculating the result
417 return fromRange==0U ? 0U : (((uint16_t)from * (uint16_t)toRange) / (uint16_t)fromRange);
418}
419
433static 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) {
434 // Stick to unsigned math for performance, so need to check for output range inversion
435 if (toLow>toHigh) {
437 } else {
439 }
440}
441
442#endif
#define DIV_ROUND_CORRECT(d, t)
Computes the denominator correction for rounding division based on our rounding behavior.
Definition maths.h:58
TESTABLE_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:175
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:395
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:346
static uint32_t percentage(uint16_t percent, uint32_t value)
Integer based percentage calculation.
Definition maths.h:298
static uint32_t div360(uint32_t n)
Optimised integer division by 360.
Definition maths.h:189
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:415
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:433
static uint32_t percentageApprox(uint16_t percent, uint32_t value)
Integer based percentage calculation: faster, but less accurate, than percentage()
Definition maths.h:252
static constexpr uint8_t ONE_HUNDRED_PCT
This is only here to eliminate magic numbers.
Definition maths.h:290
static uint32_t rshift_round(uint32_t a)
Rounded arithmetic right shift.
Definition maths.h:213
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:363
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:332
static uint16_t halfPercentage(uint8_t percent, uint16_t value)
Integer based half-percentage calculation.
Definition maths.h:311
static uint16_t div100(uint16_t n)
Performance optimised integer division by 100. I.e. same as n/100.
Definition maths.h:123
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