Skip to content

Commit 0e734f1

Browse files
authored
Extend math API (#9)
* Extend math API Signed-off-by: jparisu <javierparis@eprosima.com> * uncrustify Signed-off-by: jparisu <javierparis@eprosima.com> * Add power_of_2 to API Signed-off-by: jparisu <javierparis@eprosima.com> Signed-off-by: jparisu <javierparis@eprosima.com>
1 parent 180161b commit 0e734f1

4 files changed

Lines changed: 285 additions & 59 deletions

File tree

cpp_utils/include/cpp_utils/math/math.hpp

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,96 @@
2727
namespace eprosima {
2828
namespace utils {
2929

30+
/**
31+
* @brief Optimize % 2 operation
32+
*
33+
* @param number to calculate whether it is even
34+
*
35+
* @return whether \c number is even
36+
*/
37+
CPP_UTILS_DllAPI bool is_even(
38+
unsigned int number) noexcept;
39+
40+
/**
41+
* @brief Calculate whether the argument is a power of 2 value.
42+
*
43+
* is_power_of_2(x) <=> E(n) : 2^n = x
44+
*
45+
* @param number to calculate whether it is a power of 2
46+
*
47+
* @return whether \c number is power of 2
48+
*/
49+
CPP_UTILS_DllAPI bool is_power_of_2(
50+
unsigned int number) noexcept;
51+
3052
/**
3153
* @brief Module (%) operation with performance optimization
3254
*
3355
* This function optimizes the % operation, that executes a division, by optimizing these cases:
34-
* - If the dividend is smaller or equal than the divisor, the result is the dividend
56+
* - If the dividend is smaller than the divisor, the result is the dividend
57+
* - If the dividend is equal than the divisor, the result is 0
3558
* - If the divisor is 2, the result is the dividend % 2 calculated by a logic AND operation
3659
* - If the divisor is a power of 2, the result is calculated by a logic AND operation
60+
* - Otherwise uses % operation
3761
*
3862
* @param dividend Dividend
3963
* @param divisor Divisor (must be greater than 0 so the operation make sense)
4064
*
41-
* @attention if divisor is 0, the result is \c dividend
65+
* @pre \c divisor must not be 0
66+
*
67+
* @return The result of the operation %
4268
*
43-
* @return The result of the operation
69+
* @attention Do only use this function with non literal values. Literal values are optimized by compiler.
4470
*/
45-
CPP_UTILS_DllAPI uint32_t fast_module(
46-
uint32_t dividend,
47-
uint32_t divisor) noexcept;
71+
CPP_UTILS_DllAPI unsigned int fast_module(
72+
unsigned int dividend,
73+
unsigned int divisor) noexcept;
4874

49-
} /* namespace utils */
50-
} /* namespace eprosima */
75+
/**
76+
* @brief Integer Division (/) operation with performance optimization
77+
*
78+
* This function optimizes the / operation by optimizing these cases:
79+
* - If \c dividend is smaller or equal than the \c, the result is \c dividend
80+
* - If the \c is 2, the result is \c dividend % 2 calculated by a logic AND operation
81+
* - If the \c is a power of 2, the result is calculated by a logic AND operation
82+
* - Otherwise uses / operation
83+
*
84+
* @param dividend Dividend
85+
* @param divisor Divisor
86+
*
87+
* @pre \c divisor must not be 0
88+
*
89+
* @return The result of the operation /
90+
*
91+
* @attention Do only use this function with non literal values. Literal values are optimized by compiler.
92+
*/
93+
CPP_UTILS_DllAPI unsigned int fast_division(
94+
unsigned int dividend,
95+
unsigned int divisor) noexcept;
5196

97+
/**
98+
* @brief Calculate the sum of an arithmetic progression from an initial to a final number.
99+
*
100+
* This function uses the fast operation to calculate an arithmetic sum:
101+
* S = ((a1 + an) / 2) * (n)
102+
*
103+
* @pre \c interval must be greater than 0
104+
* @pre \c steps must be greater than 0
105+
*
106+
* @param lowest lowest element of the arithmetic progression
107+
* @param interval interval between two elements of the arithmetic progression
108+
* @param steps number of steps of the arithmetic progression
109+
*
110+
* @return The result of the sum
111+
*
112+
* EXAMPLE OF USE
113+
* 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = arithmetic_progression_sum(1, 1, 10)
114+
* 0 + 2 + 4 + 6 + 8 = arithmetic_progression_sum(0, 2, 5)
115+
*/
116+
CPP_UTILS_DllAPI unsigned int arithmetic_progression_sum(
117+
unsigned int lowest,
118+
unsigned int interval,
119+
unsigned int steps) noexcept;
52120

121+
} /* namespace utils */
122+
} /* namespace eprosima */

cpp_utils/src/cpp/math/math.cpp

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,28 @@
2020
namespace eprosima {
2121
namespace utils {
2222

23+
#include <assert.h>
24+
2325
#include <cpp_utils/math/math.hpp>
2426

25-
uint32_t fast_module(
26-
uint32_t dividend,
27-
uint32_t divisor) noexcept
27+
bool is_even(
28+
unsigned int number) noexcept
29+
{
30+
return (number & 0x1) == 0;
31+
}
32+
33+
bool is_power_of_2(
34+
unsigned int number) noexcept
35+
{
36+
return number && (!(number & (number - 1)));
37+
}
38+
39+
unsigned int fast_module(
40+
unsigned int dividend,
41+
unsigned int divisor) noexcept
2842
{
43+
assert(divisor != 0);
44+
2945
if (dividend < divisor)
3046
{
3147
// Optimize to 1 operation [if]
@@ -41,12 +57,67 @@ uint32_t fast_module(
4157
// Optimize to 4 operations [if, if, if, and]
4258
return dividend & 1;
4359
}
60+
else if (is_power_of_2(divisor))
61+
{
62+
// Optimize to ~6 operations [if, if, if, if(and), and]
63+
return dividend & (divisor - 1);
64+
}
65+
else
66+
{
67+
// Not optimum
68+
return dividend % divisor;
69+
}
70+
}
71+
72+
unsigned int fast_division(
73+
unsigned int dividend,
74+
unsigned int divisor) noexcept
75+
{
76+
assert(divisor != 0);
77+
78+
if (dividend < divisor)
79+
{
80+
// Optimize to 1 operation [if]
81+
return 0;
82+
}
83+
else if (dividend == divisor)
84+
{
85+
// Optimize to 2 operation [if, if]
86+
return 1;
87+
}
88+
else if (divisor == 1)
89+
{
90+
// Optimize to 3 operations [if, if, if]
91+
return dividend;
92+
}
93+
else if (divisor == 2)
94+
{
95+
// Optimize to 5 operations [if, if, if, if, swift]
96+
return (dividend >> 1);
97+
}
98+
else if (is_power_of_2(divisor))
99+
{
100+
while (divisor != 1)
101+
{
102+
dividend >>= 1;
103+
divisor >>= 1;
104+
}
105+
return dividend;
106+
}
44107
else
45108
{
46-
// Optimize to 7 operations [if, if, if, -, and, -, and] in case E(n){divisor = 2^n}
47-
return divisor & (divisor - 1) ? dividend % divisor : dividend& (divisor - 1);
109+
// Not optimum
110+
return dividend / divisor;
48111
}
49112
}
50113

114+
unsigned int arithmetic_progression_sum(
115+
unsigned int lowest,
116+
unsigned int interval,
117+
unsigned int steps) noexcept
118+
{
119+
return (((2 * lowest + ((steps - 1) * interval)) * steps) / 2);
120+
}
121+
51122
} /* namespace utils */
52123
} /* namespace eprosima */

cpp_utils/test/unittest/math/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ set(TEST_SOURCES
2020
)
2121

2222
set(TEST_LIST
23+
is_even
24+
is_power_of_2
2325
fast_module
26+
fast_division
27+
arithmetic_progression_sum
2428
)
2529

2630
set(TEST_EXTRA_LIBRARIES

0 commit comments

Comments
 (0)