-
-
Notifications
You must be signed in to change notification settings - Fork 128
/
utilities.c
215 lines (163 loc) · 6.5 KB
/
utilities.c
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
// This file is part of eRCaGuy_hello_world: https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world
/*
These are my favorite macros and other utility functions I frequently need in both C and C++.
See the .h file for details.
*/
#include "utilities.h"
#include <math.h> // fabs() [for double], fabsf() [for float]
// floating point comparisons
/// @brief Get a scaled version of epsilon, which is scaled to work with any size
/// `a` and `b` floating point values so that it doesn't fall out in numerical error.
////
// Todo: rename to `get_epsilon_scaled_float()`??
// For background info behind this, see: http://realtimecollisiondetection.net/blog/?p=89
static float scale_float_epsilon(float a, float b, float epsilon)
{
// float scaling_factor;
// // For very large `a` and `b`, scale epsilon UP, or else it will eventually become so small
// // relative to `a` and `b` that if falls out in the numerical error.
// scaling_factor = MAX(fabsf(a), fabsf(b));
// // For very small `a` and `b`, however, ensure `scaling_factor` doesn't get too small, or again,
// // `epsilon_scaled` will fall out in numerical error, so let's clip the scaling factor to be a
// // minimum value of 1.0f, which means the exact `epsilon` passed in is used.
// scaling_factor = MAX(scaling_factor, (float)1.0);
/////// TODO: clean this up; apply to the double func below too.
// The end result is that you have `scaling_factor = MAX(fabsf(a), fabsf(b), (float)1.0)`.
float scaling_factor = MAX3(fabsf(a), fabsf(b), (float)1.0);
float epsilon_scaled = epsilon*scaling_factor;
return epsilon_scaled;
}
// For background info behind this, see: http://realtimecollisiondetection.net/blog/?p=89
static double scale_double_epsilon(double a, double b, double epsilon)
{
double scaling_factor;
// For very large `a` and `b`, scale epsilon UP, or else it will eventually become so small
// relative to `a` and `b` that if falls out in the numerical error.
scaling_factor = MAX(fabs(a), fabs(b));
// For very small `a` and `b`, however, ensure `scaling_factor` doesn't get too small, or again,
// `epsilon_scaled` will fall out in numerical error, so let's clip the scaling factor to be a
// minimum value of 1.0f, which means the exact `epsilon` passed in is used.
scaling_factor = MAX(scaling_factor, (double)1.0);
// The end result is that you have `scaling_factor = MAX(fabs(a), fabs(b), (double)1.0)`.
double epsilon_scaled = epsilon*scaling_factor;
return epsilon_scaled;
}
bool is_float_eq(float a, float b, float epsilon)
{
float diff = a - b; // note: `-diff` is the same as `b - a`
float epsilon_scaled = scale_float_epsilon(a, b, epsilon);
return diff <= epsilon_scaled && -diff <= epsilon_scaled;
}
bool is_double_eq(double a, double b, double epsilon)
{
}
bool is_float_ne(float a, float b, float epsilon)
{
/////
// float diff = a - b; // note: `-diff` is the same as `b - a`
// float epsilon_scaled = scale_float_epsilon(a, b, epsilon);
// return diff >= epsilon_scaled || -diff >= epsilon_scaled;
return !is_float_eq(a, b, epsilon);
}
bool is_double_ne(double a, double b, double epsilon)
{
}
bool is_float_lt(float a, float b, float epsilon)
{
return a < b - scale_float_epsilon(a, b, epsilon);
}
bool is_double_lt(double a, double b, double epsilon)
{
}
bool is_float_le(float a, float b, float epsilon)
{
return a <= b + scale_float_epsilon(a, b, epsilon);
}
bool is_double_le(double a, double b, double epsilon)
{
}
bool is_float_gt(float a, float b, float epsilon)
{
return a > b + scale_float_epsilon(a, b, epsilon);
}
bool is_double_gt(double a, double b, double epsilon)
{
}
bool is_float_ge(float a, float b, float epsilon)
{
return a >= b - scale_float_epsilon(a, b, epsilon);
}
bool is_double_ge(double a, double b, double epsilon)
{
}
// From Arduino's WMath.cpp:
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void* utils_get_aligned_address(void* base_addr, size_t alignment)
{
if ((size_t)base_addr % alignment == 0)
{
// `base_addr` is already aligned
return base_addr;
}
// Else we must force `base_addr` to become aligned
void* aligned_addr = (void*)((size_t)base_addr + (alignment - (size_t)base_addr % alignment));
return aligned_addr;
}
uint64_t millis()
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
uint64_t ms = SEC_TO_MS((uint64_t)ts.tv_sec) + NS_TO_MS((uint64_t)ts.tv_nsec);
return ms;
}
uint64_t micros()
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec);
return us;
}
uint64_t nanos()
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
uint64_t ns = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
return ns;
}
int utils_rand(int min, int max)
{
static bool first_run = true;
if (first_run)
{
// seed the pseudo-random number generator with the seconds time the very first run
time_t time_now_sec = time(NULL);
srand(time_now_sec);
first_run = false;
}
int range = max - min + 1;
int random_num = rand(); // random num from 0 to RAND_MAX, inclusive
if (range > RAND_MAX)
{
static_assert(
sizeof(long int) > sizeof(int),
"This must be true or else the below mapping/scaling may have undefined overflow "
"and not work properly. In such a case, try casting to `long long int` instead of "
"just `long int`, and update this static_assert accordingly.");
// Note/todo: instead of scaling the output with `UTILS_MAP()`, I could also just obtain
// multiple random numbers (preferably, many, so as to avoid potential [I'm hypothesizing
// here] poor distribution near the extreme limits of the range) with the normal range
// (preferably each with a different seed), then sum them to get a random number with a
// much higher max range!
random_num = UTILS_MAP((long int)random_num, (long int)0, (long int)RAND_MAX, (long int)min,
(long int)max);
return random_num;
}
// This is presumably a faster approach than the map/scaling function above, so do this faster
// approach below whenever you don't **have** to do the more-complicated approach above.
random_num %= range;
random_num += min;
return random_num;
}