Skip to content

Commit bc6b360

Browse files
author
jparisu
committed
Increment math methods
Signed-off-by: jparisu <[email protected]>
1 parent e98b208 commit bc6b360

File tree

4 files changed

+213
-51
lines changed

4 files changed

+213
-51
lines changed

ddsrouter_utils/include/ddsrouter_utils/math/math.hpp

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,85 @@ namespace eprosima {
2929
namespace ddsrouter {
3030
namespace utils {
3131

32+
/**
33+
* @brief Optimize % 2 operation
34+
*
35+
* @param number
36+
*
37+
* @return whether \c number is odd
38+
*/
39+
DDSROUTER_UTILS_DllAPI bool is_even(
40+
uint32_t number) noexcept;
41+
3242
/**
3343
* @brief Module (%) operation with performance optimization
3444
*
3545
* This function optimizes the % operation, that executes a division, by optimizing these cases:
36-
* - If the dividend is smaller or equal than the divisor, the result is the dividend
46+
* - If the dividend is smaller than the divisor, the result is the dividend
47+
* - If the dividend is equal than the divisor, the result is 0
3748
* - If the divisor is 2, the result is the dividend % 2 calculated by a logic AND operation
3849
* - If the divisor is a power of 2, the result is calculated by a logic AND operation
50+
* - Otherwise uses % operation
3951
*
4052
* @param dividend Dividend
4153
* @param divisor Divisor (must be greater than 0 so the operation make sense)
4254
*
43-
* @attention if divisor is 0, the result is \c dividend
55+
* @pre \c divisor must not be 0
56+
*
57+
* @return The result of the operation %
4458
*
45-
* @return The result of the operation
59+
* @attention Do only use this function with non literal values. Literal values are optimized by compiler.
4660
*/
4761
DDSROUTER_UTILS_DllAPI uint32_t fast_module(
4862
uint32_t dividend,
4963
uint32_t divisor) noexcept;
5064

65+
/**
66+
* @brief Integer Division (/) operation with performance optimization
67+
*
68+
* This function optimizes the / operation by optimizing these cases:
69+
* - If \c dividend is smaller or equal than the \c, the result is \c dividend
70+
* - If the \c is 2, the result is \c dividend % 2 calculated by a logic AND operation
71+
* - If the \c is a power of 2, the result is calculated by a logic AND operation
72+
* - Otherwise uses / operation
73+
*
74+
* @param dividend Dividend
75+
* @param divisor Divisor
76+
*
77+
* @pre \c divisor must not be 0
78+
*
79+
* @return The result of the operation /
80+
*
81+
* @attention Do only use this function with non literal values. Literal values are optimized by compiler.
82+
*/
83+
DDSROUTER_UTILS_DllAPI uint32_t fast_division(
84+
uint32_t dividend,
85+
uint32_t divisor) noexcept;
86+
87+
/**
88+
* @brief Calculate the sum of an arithmetic progression from an initial to a final number.
89+
*
90+
* This function uses the fast operation to calculate an arithmetic sum:
91+
* S = ((a1 + an) / 2) * (n)
92+
*
93+
* @pre \c interval must be greater than 0
94+
* @pre \c steps must be greater than 0
95+
*
96+
* @param lowest lowest element of the arithmetic progression
97+
* @param interval interval between two elements of the arithmetic progression
98+
* @param steps number of steps of the arithmetic progression
99+
*
100+
* @return The result of the sum
101+
*
102+
* EXAMPLE OF USE
103+
* 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = arithmetic_progression_sum(1, 1, 10)
104+
* 0 + 2 + 4 + 6 + 8 = arithmetic_progression_sum(0, 2, 5)
105+
*/
106+
DDSROUTER_UTILS_DllAPI uint32_t arithmetic_progression_sum(
107+
uint32_t lowest,
108+
uint32_t interval,
109+
uint32_t steps) noexcept;
110+
51111
} /* namespace utils */
52112
} /* namespace ddsrouter */
53113
} /* namespace eprosima */

ddsrouter_utils/src/cpp/math/math.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,22 @@ namespace eprosima {
2121
namespace ddsrouter {
2222
namespace utils {
2323

24+
#include <assert.h>
25+
2426
#include <ddsrouter_utils/math/math.hpp>
2527

28+
bool is_even(
29+
uint32_t number) noexcept
30+
{
31+
return (number & 0x1) == 0;
32+
}
33+
2634
uint32_t fast_module(
2735
uint32_t dividend,
2836
uint32_t divisor) noexcept
2937
{
38+
assert(divisor != 0);
39+
3040
if (dividend < divisor)
3141
{
3242
// Optimize to 1 operation [if]
@@ -42,13 +52,54 @@ uint32_t fast_module(
4252
// Optimize to 4 operations [if, if, if, and]
4353
return dividend & 1;
4454
}
55+
else if (is_even(divisor))
56+
{
57+
// Optimize to 6 operations [if, if, if, if(and), and]
58+
return dividend & (divisor - 1);
59+
}
4560
else
4661
{
47-
// Optimize to 7 operations [if, if, if, -, and, -, and] in case E(n){divisor = 2^n}
48-
return divisor & (divisor - 1) ? dividend % divisor : dividend& (divisor - 1);
62+
// Not optimum
63+
return dividend % divisor;
4964
}
5065
}
5166

67+
uint32_t fast_division(
68+
uint32_t dividend,
69+
uint32_t divisor) noexcept
70+
{
71+
assert(divisor != 0);
72+
73+
if (dividend < divisor)
74+
{
75+
// Optimize to 1 operation [if]
76+
return 0;
77+
}
78+
else if (dividend == divisor)
79+
{
80+
// Optimize to 2 operation [if, if]
81+
return 1;
82+
}
83+
else if (divisor == 2)
84+
{
85+
// Optimize to 4 operations [if, if, if, swift]
86+
return (dividend >> 1);
87+
}
88+
else
89+
{
90+
// Not optimum
91+
return dividend / divisor;
92+
}
93+
}
94+
95+
uint32_t arithmetic_progression_sum(
96+
uint32_t lowest,
97+
uint32_t interval,
98+
uint32_t steps) noexcept
99+
{
100+
return (((2 * lowest + ((steps - 1) * interval)) * steps) / 2);
101+
}
102+
52103
} /* namespace utils */
53104
} /* namespace ddsrouter */
54105
} /* namespace eprosima */

ddsrouter_utils/test/unittest/math/CMakeLists.txt

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

2222
set(TEST_LIST
23+
is_even
2324
fast_module
25+
fast_division
26+
arithmetic_progression_sum
2427
)
2528

2629
set(TEST_EXTRA_LIBRARIES

ddsrouter_utils/test/unittest/math/mathTest.cpp

Lines changed: 94 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,55 @@ namespace ddsrouter {
2626
namespace utils {
2727
namespace test {
2828

29-
void compare_fast_module(
29+
uint32_t NUMBERS_TO_TEST = 1000;
30+
uint32_t NUMBERS_TO_TEST_SHORT = 100;
31+
32+
bool compare_is_even(
33+
uint32_t number)
34+
{
35+
return (
36+
is_even(number)
37+
==
38+
number % 2 == 0);
39+
}
40+
41+
bool compare_fast_module(
42+
uint32_t dividend,
43+
uint32_t divisor)
44+
{
45+
return (
46+
fast_module(dividend, divisor)
47+
==
48+
dividend % divisor);
49+
}
50+
51+
bool compare_fast_division(
3052
uint32_t dividend,
3153
uint32_t divisor)
3254
{
33-
ASSERT_EQ(fast_module(dividend, divisor), dividend % divisor) << dividend << " % " << divisor;
55+
return (
56+
fast_division(dividend, divisor)
57+
==
58+
dividend / divisor);
59+
}
60+
61+
bool compare_arithmetic_progression_sum(
62+
uint32_t lowest,
63+
uint32_t interval,
64+
uint32_t steps)
65+
{
66+
uint32_t current_number = lowest;
67+
uint32_t real_result = 0;
68+
for (int i = 0; i < steps; ++i)
69+
{
70+
real_result += current_number;
71+
current_number += interval;
72+
}
73+
74+
return (
75+
arithmetic_progression_sum(lowest, interval, steps)
76+
==
77+
real_result);
3478
}
3579

3680
} /* namespace test */
@@ -39,62 +83,66 @@ void compare_fast_module(
3983
} /* namespace eprosima */
4084

4185
/**
42-
* Test \c is_file_accesible method
43-
*
44-
* CASES:
45-
* - dividend lower than divisor
46-
* - dividend equal to divisor
47-
* - divisor = 2
48-
* - divisor = 2^N
49-
* - divisor even no 2^N
50-
* - divisor odd
86+
* Test \c is_even method
5187
*/
52-
TEST(mathTest, fast_module)
88+
TEST(mathTest, is_even)
5389
{
54-
// dividend lower than divisor
90+
// calculate module in many cases
91+
for (uint32_t number = 0; number < test::NUMBERS_TO_TEST; ++number)
5592
{
56-
test::compare_fast_module(3, 4);
57-
test::compare_fast_module(0, 4);
58-
test::compare_fast_module(101223, 20921341);
59-
}
60-
61-
// dividend equal to divisor
62-
{
63-
test::compare_fast_module(3, 3);
64-
test::compare_fast_module(4, 4);
65-
test::compare_fast_module(66666, 66666);
66-
}
67-
68-
// divisor = 2
69-
{
70-
test::compare_fast_module(3, 2);
71-
test::compare_fast_module(4, 2);
72-
test::compare_fast_module(66666, 2);
73-
test::compare_fast_module(431253426, 2);
93+
ASSERT_TRUE(test::compare_is_even(number))
94+
<< number;
7495
}
96+
}
7597

76-
// divisor = 2^N
98+
/**
99+
* Test \c fast_module method
100+
*/
101+
TEST(mathTest, fast_module)
102+
{
103+
// calculate module in many cases
104+
for (uint32_t dividend = 0; dividend < test::NUMBERS_TO_TEST; ++dividend)
77105
{
78-
test::compare_fast_module(3, 4);
79-
test::compare_fast_module(32, 8);
80-
test::compare_fast_module(66666, 128);
81-
test::compare_fast_module(431253426, 2048);
106+
for (uint32_t divisor = 1; divisor < test::NUMBERS_TO_TEST; ++divisor)
107+
{
108+
ASSERT_TRUE(test::compare_fast_module(dividend, divisor))
109+
<< dividend << " % " << divisor;
110+
}
82111
}
112+
}
83113

84-
// divisor even no 2^N
114+
/**
115+
* Test \c fast_division method
116+
*/
117+
TEST(mathTest, fast_division)
118+
{
119+
// calculate module in many cases
120+
for (uint32_t dividend = 0; dividend < test::NUMBERS_TO_TEST; ++dividend)
85121
{
86-
test::compare_fast_module(3, 8);
87-
test::compare_fast_module(12, 10);
88-
test::compare_fast_module(66666, 120);
89-
test::compare_fast_module(431253426, 2040);
122+
for (uint32_t divisor = 1; divisor < test::NUMBERS_TO_TEST; ++divisor)
123+
{
124+
ASSERT_TRUE(test::compare_fast_division(dividend, divisor))
125+
<< dividend << " % " << divisor;
126+
}
90127
}
128+
}
91129

92-
// divisor odd
130+
/**
131+
* Test \c arithmetic_progression_sum method
132+
*/
133+
TEST(mathTest, arithmetic_progression_sum)
134+
{
135+
// calculate module in many cases
136+
for (uint32_t lowest = 0; lowest < test::NUMBERS_TO_TEST_SHORT; ++lowest)
93137
{
94-
test::compare_fast_module(3, 5);
95-
test::compare_fast_module(12, 11);
96-
test::compare_fast_module(66666, 127);
97-
test::compare_fast_module(431253426, 2041);
138+
for (uint32_t interval = 1; interval < test::NUMBERS_TO_TEST_SHORT; ++interval)
139+
{
140+
for (uint32_t steps = 1; steps < test::NUMBERS_TO_TEST_SHORT; ++steps)
141+
{
142+
ASSERT_TRUE(test::compare_arithmetic_progression_sum(lowest, interval, steps))
143+
<< lowest << " , " << interval << " , " << steps;
144+
}
145+
}
98146
}
99147
}
100148

0 commit comments

Comments
 (0)