Skip to content

Commit 87b82f5

Browse files
committed
sys: util: add SCALE() to change a value range
This permits to make a computation in a larger range to gain precision: int32_t tmp_in = SCALE(in, -100, 100, INT32_MIN, INT32_MAX); Or convert between two unit systems: uint32_t fahrenheit = SCALE(celsius, 0, 100, 32, 212); Signed-off-by: Josuah Demangeon <me@josuah.net>
1 parent 9403b08 commit 87b82f5

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

include/zephyr/sys/util.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,30 @@ extern "C" {
407407
#define CLAMP(val, low, high) (((val) <= (low)) ? (low) : MIN(val, high))
408408
#endif
409409

410+
/**
411+
* @brief Linearly maps a value from one scale to another.
412+
*
413+
* A value is considered to fit on an input range, and is scaled to
414+
* the same proportion of the output range.
415+
* The ranges are described by their minimum and maximum values.
416+
*
417+
* @note To avoid an overflow, the number of bits of the input and output range
418+
* must be equal or lower to 61. For instance, an input range with min and max
419+
* values between INT8_MIN and INT8_MAX, then the output range must have
420+
* values within INT53_MIN and INT53_MAX.
421+
*
422+
* @note The size of the output range can be [0, 0].
423+
* The size of the input range must be greater than 0
424+
*
425+
* @param i The value to convert from the input range to the output range.
426+
* @param imin The minimum value of the input range.
427+
* @param imax The maximum value of the input range.
428+
* @param omin The minimum value of the output range.
429+
* @param omax The maximum value of the output range.
430+
*/
431+
#define SCALE(i, imin, imax, omin, omax) \
432+
(((int64_t)(i) - (imin)) * ((int64_t)(omax) - (omin)) / ((int64_t)(imax) - (imin)) + (omin))
433+
410434
/**
411435
* @brief Checks if a value is within range.
412436
*

tests/lib/sys_util/src/main.c

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,73 @@
1313
* @{
1414
*/
1515

16+
ZTEST(sys_util, test_SCALE)
17+
{
18+
/* Test for a few arbitrary values */
19+
zassert_equal(SCALE(3, 0, 10, 0, 100), 30);
20+
zassert_equal(SCALE(-3, -10, 0, -100, 0), -30);
21+
zassert_equal(SCALE(10, -100, 100, -10, 10), 1);
22+
zassert_equal(SCALE(0, -10, 40, -50, 0), -40);
23+
zassert_equal(SCALE(0, -128, 127, 0, 2), 1);
24+
zassert_equal(SCALE(5, -50, 5000, -1000, 10), -989);
25+
26+
/* Test for i = 1..60 and o = 60..1 */
27+
for (int i = 1; i < 61; i++) {
28+
int o = 61 - i;
29+
int64_t imin = -(1ll << i);
30+
int64_t imax = +(1ll << i);
31+
int64_t omin = -(1ll << o);
32+
int64_t omax = +(1ll << o);
33+
34+
/* Special case: the output range can be [0, 0] */
35+
36+
zassert_equal(SCALE(imin, imin, imax, 0, 0), 0);
37+
zassert_equal(SCALE(0, imin, imax, 0, 0), 0);
38+
zassert_equal(SCALE(imax, imin, imax, 0, 0), 0);
39+
40+
zassert_equal(SCALE(0, 0, imax, 0, 0), 0);
41+
zassert_equal(SCALE(imax, 0, imax, 0, 0), 0);
42+
43+
zassert_equal(SCALE(imin, imin, 0, 0, 0), 0);
44+
zassert_equal(SCALE(0, imin, 0, 0, 0), 0);
45+
46+
/* Test the extreme of all known cases */
47+
48+
zassert_equal(SCALE(imin, imin, imax, omin, omax), omin);
49+
zassert_equal(SCALE(0, imin, imax, omin, omax), 0);
50+
zassert_equal(SCALE(imax, imin, imax, omin, omax), omax);
51+
52+
zassert_equal(SCALE(0, 0, imax, omin, omax), omin);
53+
zassert_equal(SCALE(imax, 0, imax, omin, omax), omax);
54+
55+
zassert_equal(SCALE(imin, imin, 0, omin, omax), omin);
56+
zassert_equal(SCALE(0, imin, 0, omin, omax), omax);
57+
58+
zassert_equal(SCALE(imin, imin, imax, 0, omax), 0);
59+
zassert_equal(SCALE(0, imin, imax, 0, omax), omax / 2);
60+
zassert_equal(SCALE(imax, imin, imax, 0, omax), omax);
61+
62+
zassert_equal(SCALE(imin, imin, imax, omin, 0), omin);
63+
zassert_equal(SCALE(0, imin, imax, omin, 0), omin / 2);
64+
zassert_equal(SCALE(imax, imin, imax, omin, 0), 0);
65+
66+
zassert_equal(SCALE(0, 0, imax, 0, omax), 0);
67+
zassert_equal(SCALE(imax, 0, imax, 0, omax), omax);
68+
69+
zassert_equal(SCALE(0, 0, imax, omin, 0), omin);
70+
zassert_equal(SCALE(imax, 0, imax, omin, 0), 0);
71+
72+
zassert_equal(SCALE(imin, imin, 0, 0, omax), 0);
73+
zassert_equal(SCALE(0, imin, 0, 0, omax), omax);
74+
75+
zassert_equal(SCALE(imin, imin, 0, omin, 0), omin);
76+
zassert_equal(SCALE(0, imin, 0, omin, 0), 0);
77+
78+
zassert_equal(SCALE(0, 0, imax, 0, omax), 0);
79+
zassert_equal(SCALE(imax, 0, imax, 0, omax), omax);
80+
}
81+
}
82+
1683
/**
1784
* @brief Test wait_for works as expected with typical use cases
1885
*
@@ -71,7 +138,6 @@ ZTEST(sys_util, test_NUM_VA_ARGS_LESS_1)
71138
* @}
72139
*/
73140

74-
75141
/**
76142
* @defgroup sys_util_tests Sys Util Tests
77143
* @ingroup all_tests

0 commit comments

Comments
 (0)