analog: Implement multiplication for sr_rational

This commit is contained in:
Stefan Brüns 2016-04-24 02:48:26 +02:00 committed by Uwe Hermann
parent bdba362695
commit ee1b6054d6
3 changed files with 117 additions and 0 deletions

View File

@ -34,6 +34,8 @@ SR_API int sr_analog_unit_to_string(const struct sr_datafeed_analog *analog,
char **result);
SR_API void sr_rational_set(struct sr_rational *r, int64_t p, uint64_t q);
SR_API int sr_rational_eq(const struct sr_rational *a, const struct sr_rational *b);
SR_API int sr_rational_mult(struct sr_rational *res, const struct sr_rational *a,
const struct sr_rational *b);
/*--- backend.c -------------------------------------------------------------*/

View File

@ -433,4 +433,77 @@ SR_API int sr_rational_eq(const struct sr_rational *a, const struct sr_rational
#endif
}
/**
* Multiply two sr_rational
*
* @param[in] a First value
* @param[in] b Second value
* @param[out] res Result
*
* The resulting nominator/denominator are reduced if the result would not fit
* otherwise. If the resulting nominator/denominator are relatively prime,
* this may not be possible.
*
* @retval SR_OK Success.
* @retval SR_ERR_ARG Resulting value to large
*
* @since 0.5.0
*/
SR_API int sr_rational_mult(struct sr_rational *res, const struct sr_rational *a,
const struct sr_rational *b)
{
#ifdef HAVE___INT128_T
__int128_t p;
__uint128_t q;
p = (__int128_t)(a->p) * (__int128_t)(b->p);
q = (__uint128_t)(a->q) * (__uint128_t)(b->q);
if ((p > INT64_MAX) || (p < INT64_MIN) || (q > UINT64_MAX)) {
while (!((p & 1) || (q & 1))) {
p /= 2;
q /= 2;
}
}
if ((p > INT64_MAX) || (p < INT64_MIN) || (q > UINT64_MAX)) {
// TODO: determine gcd to do further reduction
return SR_ERR_ARG;
}
res->p = (int64_t)(p);
res->q = (uint64_t)(q);
return SR_OK;
#else
struct sr_int128_t p;
struct sr_uint128_t q;
mult_int64(&p, a->p, b->p);
mult_uint64(&q, a->q, b->q);
while (!(p.low & 1) && !(q.low & 1)) {
p.low /= 2;
if (p.high & 1) p.low |= (1ll << 63);
p.high >>= 1;
q.low /= 2;
if (q.high & 1) q.low |= (1ll << 63);
q.high >>= 1;
}
if (q.high)
return SR_ERR_ARG;
if ((p.high >= 0) && (p.low > INT64_MAX))
return SR_ERR_ARG;
if (p.high < -1)
return SR_ERR_ARG;
res->p = (int64_t)p.low;
res->q = q.low;
return SR_OK;
#endif
}
/** @} */

View File

@ -225,6 +225,47 @@ START_TEST(test_cmp_rational)
}
END_TEST
START_TEST(test_mult_rational)
{
const struct sr_rational r[][3] = {
/* a * b = c */
{ { 1, 1 }, { 1, 1 }, { 1, 1 }},
{ { 2, 1 }, { 3, 1 }, { 6, 1 }},
{ { 1, 2 }, { 2, 1 }, { 1, 1 }},
/* Test negative numbers */
{ { -1, 2 }, { 2, 1 }, { -1, 1 }},
{ { -1, 2 }, { -2, 1 }, { 1, 1 }},
{ { -(1ll<<20), (1ll<<10) }, { -(1ll<<20), 1 }, { (1ll<<30), 1 }},
/* Test reduction */
{ { INT32_MAX, (1ll<<12) }, { (1<<2), 1 }, { INT32_MAX, (1ll<<10) }},
{ { INT64_MAX, (1ll<<63) }, { (1<<3), 1 }, { INT64_MAX, (1ll<<60) }},
/* Test large numbers */
{ { (1ll<<40), (1ll<<10) }, { (1ll<<30), 1 }, { (1ll<<60), 1 }},
{ { -(1ll<<40), (1ll<<10) }, { -(1ll<<30), 1 }, { (1ll<<60), 1 }},
{ { 1000, 1 }, { 8000, 1 }, { 8000000, 1 }},
{ { 10000, 1 }, { 80000, 1 }, { 800000000, 1 }},
{ { 10000*3, 4 }, { 80000*3, 1 }, { 200000000*9, 1 }},
{ { 1, 1000 }, { 1, 8000 }, { 1, 8000000 }},
{ { 1, 10000 }, { 1, 80000 }, { 1, 800000000 }},
{ { 4, 10000*3 }, { 1, 80000*3 }, { 1, 200000000*9 }},
{ { -10000*3, 4 }, { 80000*3, 1 }, { -200000000*9, 1 }},
{ { 10000*3, 4 }, { -80000*3, 1 }, { -200000000*9, 1 }},
};
for (unsigned i = 0; i < ARRAY_SIZE(r); i++) {
struct sr_rational res;
int rc = sr_rational_mult(&res, &r[i][0], &r[i][1]);
fail_unless(rc == SR_OK);
fail_unless(sr_rational_eq(&res, &r[i][2]) == 1,
"sr_rational_mult() failed: [%d] %ld/%lu != %ld/%lu.",
i, res.p, res.q, r[i][2].p, r[i][2].q);
}
}
END_TEST
Suite *suite_analog(void)
{
Suite *s;
@ -240,6 +281,7 @@ Suite *suite_analog(void)
tcase_add_test(tc, test_set_rational);
tcase_add_test(tc, test_set_rational_null);
tcase_add_test(tc, test_cmp_rational);
tcase_add_test(tc, test_mult_rational);
suite_add_tcase(s, tc);
return s;