Merge pull request #4066 from kaspar030/add_div
sys: add div.h, a header containing integer division functions
This commit is contained in:
commit
ac1fcde44e
112
sys/include/div.h
Normal file
112
sys/include/div.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Provides integer division functions
|
||||
*
|
||||
* This header provides some integer division functions that can be used
|
||||
* to prevent linking in compiler-generated ones, which are often larger.
|
||||
*
|
||||
* @file
|
||||
* @ingroup sys_util
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef DIV_H
|
||||
#define DIV_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Integer divide val by 15625
|
||||
*
|
||||
* @pre val <= 16383999997
|
||||
*
|
||||
* @param[in] val dividend
|
||||
* @return (val / 15625)
|
||||
*/
|
||||
static inline uint64_t div_u64_by_15625(uint64_t val)
|
||||
{
|
||||
/* a higher value would overflow 2^64 in the multiplication that follows */
|
||||
assert(val <= 16383999997LLU);
|
||||
|
||||
return (val * 0x431bde83UL) >> (12 + 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Integer divide val by 1000000
|
||||
*
|
||||
* @pre val <= 1048575999808
|
||||
*
|
||||
* @param[in] val dividend
|
||||
* @return (val / 1000000)
|
||||
*/
|
||||
static inline uint64_t div_u64_by_1000000(uint64_t val)
|
||||
{
|
||||
/* a higher value would overflow 2^64 in the multiplication that follows */
|
||||
assert(val <= 1048575999808LLU);
|
||||
|
||||
return div_u64_by_15625(val>>6);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Divide val by (15625/512)
|
||||
*
|
||||
* This is used to quantize a 1MHz value to the closest 32768Hz value,
|
||||
* e.g., for timers.
|
||||
*
|
||||
* The algorithm actually multiplies by 512 first, then divides by 15625,
|
||||
* keeping the result closer to a floored floating point division.
|
||||
*
|
||||
* @param[in] val dividend
|
||||
* @return (val / (15625/512))
|
||||
*/
|
||||
static inline uint32_t div_u32_by_15625div512(uint32_t val)
|
||||
{
|
||||
return ((uint64_t)(val) * 0x431bde83ul) >> (12 + 32 - 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Integer divide val by 10
|
||||
*
|
||||
* @param[in] n dividend
|
||||
* @return (n / 10)
|
||||
*/
|
||||
static inline uint32_t div_u32_by_10(uint32_t n) {
|
||||
uint32_t q, r;
|
||||
q = (n >> 1) + (n >> 2);
|
||||
q = q + (q >> 4);
|
||||
q = q + (q >> 8);
|
||||
q = q + (q >> 16);
|
||||
q = q >> 3;
|
||||
r = n - (((q << 2) + q) << 1);
|
||||
return q + (r > 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Modulo 10
|
||||
*
|
||||
* @param[in] n dividend
|
||||
* @return (n % 10)
|
||||
*/
|
||||
static inline uint32_t div_u32_mod_10(uint32_t n)
|
||||
{
|
||||
return n - (div_u32_by_10(n)*10);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/** @} */
|
||||
#endif /* DIV_H */
|
||||
@ -21,6 +21,7 @@
|
||||
#include "mutex.h"
|
||||
#include "thread.h"
|
||||
#include "irq.h"
|
||||
#include "div.h"
|
||||
|
||||
#include "timex.h"
|
||||
|
||||
@ -150,21 +151,11 @@ void xtimer_set_wakeup64(xtimer_t *timer, uint64_t offset, kernel_pid_t pid)
|
||||
_xtimer_set64(timer, offset, offset >> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* see http://www.hackersdelight.org/magic.htm.
|
||||
* This is to avoid using long integer division functions
|
||||
* the compiler otherwise links in.
|
||||
*/
|
||||
static inline uint64_t _us_to_sec(uint64_t us)
|
||||
{
|
||||
return (unsigned long long)(us * 0x431bde83) >> (0x12 + 32);
|
||||
}
|
||||
|
||||
void xtimer_now_timex(timex_t *out)
|
||||
{
|
||||
uint64_t now = xtimer_now64();
|
||||
|
||||
out->seconds = _us_to_sec(now);
|
||||
out->seconds = div_u64_by_1000000(now);
|
||||
out->microseconds = now - (out->seconds * SEC_IN_USEC);
|
||||
}
|
||||
|
||||
|
||||
1
tests/unittests/tests-div/Makefile
Normal file
1
tests/unittests/tests-div/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
120
tests/unittests/tests-div/tests-div.c
Normal file
120
tests/unittests/tests-div/tests-div.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "embUnit.h"
|
||||
#include "tests-div.h"
|
||||
|
||||
#include "div.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static uint32_t u32_test_values[] = {
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
32,
|
||||
15625,
|
||||
15625LU*5,
|
||||
(15625LU*5)+1,
|
||||
0xffff,
|
||||
0xffff<<10,
|
||||
0xffffffff
|
||||
};
|
||||
|
||||
static uint64_t u64_test_values[] = {
|
||||
0xffffffffULL+1
|
||||
};
|
||||
|
||||
#define N_U32_VALS (sizeof(u32_test_values)/sizeof(uint32_t))
|
||||
#define N_U64_VALS (sizeof(u64_test_values)/sizeof(uint64_t))
|
||||
|
||||
static void test_div_u64_by_15625(void)
|
||||
{
|
||||
for (unsigned i = 0; i < N_U32_VALS; i++) {
|
||||
DEBUG("Dividing %"PRIu32" by 15625...\n", u32_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u64_by_15625(u32_test_values[i]),
|
||||
u32_test_values[i]/15625);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < N_U64_VALS; i++) {
|
||||
DEBUG("Dividing %"PRIu64" by 15625...\n", u64_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u64_by_15625(u64_test_values[i]),
|
||||
u64_test_values[i]/15625);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_div_u32_by_15625div512(void)
|
||||
{
|
||||
for (unsigned i = 0; i < N_U32_VALS; i++) {
|
||||
DEBUG("Dividing %"PRIu32" by (15625/512)...\n", u32_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u32_by_15625div512(u32_test_values[i]),
|
||||
(uint64_t)u32_test_values[i]*512LU/15625);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_div_u64_by_1000000(void)
|
||||
{
|
||||
for (unsigned i = 0; i < N_U32_VALS; i++) {
|
||||
DEBUG("Dividing %"PRIu32" by 1000000...\n", u32_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u64_by_1000000(u32_test_values[i]),
|
||||
u32_test_values[i]/1000000);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < N_U64_VALS; i++) {
|
||||
DEBUG("Dividing %"PRIu64" by 1000000...\n", u64_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u64_by_1000000(u64_test_values[i]),
|
||||
u64_test_values[i]/1000000U);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_div_u32_by_10(void)
|
||||
{
|
||||
for (unsigned i = 0; i < N_U32_VALS; i++) {
|
||||
DEBUG("Dividing %"PRIu32" by 10...\n", u32_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u32_by_10(u32_test_values[i]),
|
||||
u32_test_values[i]/10);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_div_u32_mod_10(void)
|
||||
{
|
||||
for (unsigned i = 0; i < N_U32_VALS; i++) {
|
||||
DEBUG("Calculating %"PRIu32" % 10...\n", u32_test_values[i]);
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
div_u32_mod_10(u32_test_values[i]),
|
||||
u32_test_values[i]%10);
|
||||
}
|
||||
}
|
||||
|
||||
Test *tests_div_tests(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_div_u64_by_15625),
|
||||
new_TestFixture(test_div_u32_by_15625div512),
|
||||
new_TestFixture(test_div_u64_by_1000000),
|
||||
new_TestFixture(test_div_u32_by_10),
|
||||
new_TestFixture(test_div_u32_mod_10),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(div_tests, NULL, NULL, fixtures);
|
||||
|
||||
return (Test *)&div_tests;
|
||||
}
|
||||
|
||||
void tests_div(void)
|
||||
{
|
||||
TESTS_RUN(tests_div_tests());
|
||||
}
|
||||
43
tests/unittests/tests-div/tests-div.h
Normal file
43
tests/unittests/tests-div/tests-div.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup unittests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unittests for the ``div`` header
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*/
|
||||
#ifndef TESTS_DIV_H_
|
||||
#define TESTS_DIV_H_
|
||||
#include "embUnit/embUnit.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The entry point of this test suite.
|
||||
*/
|
||||
void tests_div(void);
|
||||
|
||||
/**
|
||||
* @brief Generates tests for div
|
||||
*
|
||||
* @return embUnit tests if successful, NULL if not.
|
||||
*/
|
||||
Test *tests_div_tests(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TESTS_DIV_H_ */
|
||||
/** @} */
|
||||
Loading…
x
Reference in New Issue
Block a user