1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/frac/frac.c
Karl Fessel 4445faaa3a core/shed: remove not needed bitarithm include add missing
bitarithm.h is not needed for the interface of shed but may cause conflicts
due to different definitions of SETBIT and CLRBIT

common implementations are: (value, offset) xor (value, mask) bitarithm
implements the later

frac.c and nrf52/usbdev.c use bitarithm.h but where missing the include

sam0/rtt.c defined a bit using mask from bitarithm,
changed that to the soulution used in sam0/rtc.c
2020-02-05 12:45:29 +01:00

159 lines
4.0 KiB
C

/**
* Copyright (C) 2018 Eistec AB
*
* 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.
*
* @ingroup sys_frac
* @{
* @file
* @brief Integer fraction function implementations
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/
#include <stdint.h>
#include <stdio.h>
#include "assert.h"
#include "frac.h"
#include "bitarithm.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/**
* @brief compute greatest common divisor of @p u and @p v
*
* @param[in] u first operand
* @param[in] v second operand
*
* @return Greatest common divisor of @p u and @p v
*/
static uint32_t gcd32(uint32_t u, uint32_t v)
{
/* Source: https://en.wikipedia.org/wiki/Binary_GCD_algorithm#Iterative_version_in_C */
unsigned shift;
/* GCD(0,v) == v; GCD(u,0) == u, GCD(0,0) == 0 */
if (u == 0) {
return v;
}
if (v == 0) {
return u;
}
/* Let shift := log2 K, where K is the greatest power of 2
* dividing both u and v. */
for (shift = 0; ((u | v) & 1) == 0; ++shift) {
u >>= 1;
v >>= 1;
}
/* remove all factors of 2 in u */
while ((u & 1) == 0) {
u >>= 1;
}
/* From here on, u is always odd. */
do {
/* remove all factors of 2 in v -- they are not common */
/* note: v is not zero, so while will terminate */
while ((v & 1) == 0) {
v >>= 1;
}
/* Now u and v are both odd. Swap if necessary so u <= v,
* then set v = v - u (which is even). */
if (u > v) {
/* Swap u and v */
uint32_t t = v;
v = u;
u = t;
}
v = v - u; /* Here v >= u */
} while (v != 0);
/* restore common factors of 2 */
return u << shift;
}
uint32_t frac_long_divide(uint32_t num, uint32_t den, int *prec, uint32_t *rem)
{
/* Binary long division with adaptive number of fractional bits */
/* The result will be a Qx.y number where x is the number of bits in the
* integer part and y = 64 - x. Similar to floating point, except the result
* is unsigned, and we can only represent numbers in the range 2**-32..(2**32 - 1) */
assert(den); /* divide by zero */
uint32_t q = 0; /* Quotient */
uint64_t r = 0; /* Remainder */
if (prec) {
*prec = 0;
}
if (num == 0) {
if (rem) {
*rem = 0;
}
return 0;
}
unsigned p = bitarithm_msb(num);
int i_bits = p + 1; /* Number of integer bits in the result */
uint32_t num_mask = (1ul << p);
for (unsigned k = 0; k < (64u + p); ++k) {
r <<= 1;
q <<= 1;
if (num & num_mask) {
r |= 1;
}
num_mask >>= 1;
if (r >= den) {
r -= den;
q |= 1;
}
if (q == 0) {
--i_bits;
}
if (q & (1ul << 31u)) {
/* result register is full */
break;
}
if ((r == 0) && (num == 0)) {
/* divides evenly */
break;
}
}
if (r > 0) {
++q;
}
if (prec) {
*prec = i_bits;
}
if (rem) {
*rem = r;
}
return q;
}
void frac_init(frac_t *frac, uint32_t num, uint32_t den)
{
DEBUG("frac_init32(%p, %" PRIu32 ", %" PRIu32 ")\n", (const void *)frac, num, den);
assert(den);
/* Reduce the fraction to shortest possible form by dividing by the greatest
* common divisor */
uint32_t gcd = gcd32(num, den);
/* Divide den and num by their greatest common divisor */
den /= gcd;
num /= gcd;
int prec = 0;
uint32_t rem = 0;
frac->frac = frac_long_divide(num, den, &prec, &rem);
frac->shift = (sizeof(frac->frac) * 8) - prec;
DEBUG("frac_init32: gcd = %" PRIu32 " num = %" PRIu32 " den = %" PRIu32 " frac = 0x%08" PRIx32 " shift = %02d, rem = 0x%08" PRIx32 "\n",
gcd, num, den, frac->frac, frac->shift, rem);
}