Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 78 additions & 8 deletions cpp_utils/include/cpp_utils/math/math.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,96 @@
namespace eprosima {
namespace utils {

/**
* @brief Optimize % 2 operation
*
* @param number to calculate whether it is even
*
* @return whether \c number is even
*/
CPP_UTILS_DllAPI bool is_even(
unsigned int number) noexcept;

/**
* @brief Calculate whether the argument is a power of 2 value.
*
* is_power_of_2(x) <=> E(n) : 2^n = x
*
* @param number to calculate whether it is a power of 2
*
* @return whether \c number is power of 2
*/
CPP_UTILS_DllAPI bool is_power_of_2(
unsigned int number) noexcept;

/**
* @brief Module (%) operation with performance optimization
*
* This function optimizes the % operation, that executes a division, by optimizing these cases:
* - If the dividend is smaller or equal than the divisor, the result is the dividend
* - If the dividend is smaller than the divisor, the result is the dividend
* - If the dividend is equal than the divisor, the result is 0
* - If the divisor is 2, the result is the dividend % 2 calculated by a logic AND operation
* - If the divisor is a power of 2, the result is calculated by a logic AND operation
* - Otherwise uses % operation
*
* @param dividend Dividend
* @param divisor Divisor (must be greater than 0 so the operation make sense)
*
* @attention if divisor is 0, the result is \c dividend
* @pre \c divisor must not be 0
*
* @return The result of the operation %
*
* @return The result of the operation
* @attention Do only use this function with non literal values. Literal values are optimized by compiler.
*/
CPP_UTILS_DllAPI uint32_t fast_module(
uint32_t dividend,
uint32_t divisor) noexcept;
CPP_UTILS_DllAPI unsigned int fast_module(
unsigned int dividend,
unsigned int divisor) noexcept;

} /* namespace utils */
} /* namespace eprosima */
/**
* @brief Integer Division (/) operation with performance optimization
*
* This function optimizes the / operation by optimizing these cases:
* - If \c dividend is smaller or equal than the \c, the result is \c dividend
* - If the \c is 2, the result is \c dividend % 2 calculated by a logic AND operation
* - If the \c is a power of 2, the result is calculated by a logic AND operation
* - Otherwise uses / operation
*
* @param dividend Dividend
* @param divisor Divisor
*
* @pre \c divisor must not be 0
*
* @return The result of the operation /
*
* @attention Do only use this function with non literal values. Literal values are optimized by compiler.
*/
CPP_UTILS_DllAPI unsigned int fast_division(
unsigned int dividend,
unsigned int divisor) noexcept;

/**
* @brief Calculate the sum of an arithmetic progression from an initial to a final number.
*
* This function uses the fast operation to calculate an arithmetic sum:
* S = ((a1 + an) / 2) * (n)
*
* @pre \c interval must be greater than 0
* @pre \c steps must be greater than 0
*
* @param lowest lowest element of the arithmetic progression
* @param interval interval between two elements of the arithmetic progression
* @param steps number of steps of the arithmetic progression
*
* @return The result of the sum
*
* EXAMPLE OF USE
* 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = arithmetic_progression_sum(1, 1, 10)
* 0 + 2 + 4 + 6 + 8 = arithmetic_progression_sum(0, 2, 5)
*/
CPP_UTILS_DllAPI unsigned int arithmetic_progression_sum(
unsigned int lowest,
unsigned int interval,
unsigned int steps) noexcept;

} /* namespace utils */
} /* namespace eprosima */
81 changes: 76 additions & 5 deletions cpp_utils/src/cpp/math/math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,28 @@
namespace eprosima {
namespace utils {

#include <assert.h>

#include <cpp_utils/math/math.hpp>

uint32_t fast_module(
uint32_t dividend,
uint32_t divisor) noexcept
bool is_even(
unsigned int number) noexcept
{
return (number & 0x1) == 0;
}

bool is_power_of_2(
unsigned int number) noexcept
{
return number && (!(number & (number - 1)));
}

unsigned int fast_module(
unsigned int dividend,
unsigned int divisor) noexcept
{
assert(divisor != 0);

if (dividend < divisor)
{
// Optimize to 1 operation [if]
Expand All @@ -41,12 +57,67 @@ uint32_t fast_module(
// Optimize to 4 operations [if, if, if, and]
return dividend & 1;
}
else if (is_power_of_2(divisor))
{
// Optimize to ~6 operations [if, if, if, if(and), and]
return dividend & (divisor - 1);
}
else
{
// Not optimum
return dividend % divisor;
}
}

unsigned int fast_division(
unsigned int dividend,
unsigned int divisor) noexcept
{
assert(divisor != 0);

if (dividend < divisor)
{
// Optimize to 1 operation [if]
return 0;
}
else if (dividend == divisor)
{
// Optimize to 2 operation [if, if]
return 1;
}
else if (divisor == 1)
{
// Optimize to 3 operations [if, if, if]
return dividend;
}
else if (divisor == 2)
{
// Optimize to 5 operations [if, if, if, if, swift]
return (dividend >> 1);
}
else if (is_power_of_2(divisor))
{
while (divisor != 1)
{
dividend >>= 1;
divisor >>= 1;
}
return dividend;
}
else
{
// Optimize to 7 operations [if, if, if, -, and, -, and] in case E(n){divisor = 2^n}
return divisor & (divisor - 1) ? dividend % divisor : dividend& (divisor - 1);
// Not optimum
return dividend / divisor;
}
}

unsigned int arithmetic_progression_sum(
unsigned int lowest,
unsigned int interval,
unsigned int steps) noexcept
{
return (((2 * lowest + ((steps - 1) * interval)) * steps) / 2);
}

} /* namespace utils */
} /* namespace eprosima */
4 changes: 4 additions & 0 deletions cpp_utils/test/unittest/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ set(TEST_SOURCES
)

set(TEST_LIST
is_even
is_power_of_2
fast_module
fast_division
arithmetic_progression_sum
)

set(TEST_EXTRA_LIBRARIES
Expand Down
Loading