From: Stefan BrĂ¼ns Date: Sun, 24 Apr 2016 00:48:26 +0000 (+0200) Subject: analog: Implement multiplication for sr_rational X-Git-Tag: libsigrok-0.5.0~399 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=ee1b6054d6d6a00698ddae421eae63019c452b6d;p=libsigrok.git analog: Implement multiplication for sr_rational --- diff --git a/include/libsigrok/proto.h b/include/libsigrok/proto.h index 0dd594ed..bd725057 100644 --- a/include/libsigrok/proto.h +++ b/include/libsigrok/proto.h @@ -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 -------------------------------------------------------------*/ diff --git a/src/analog.c b/src/analog.c index a04844d6..7c0dde1b 100644 --- a/src/analog.c +++ b/src/analog.c @@ -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 +} + /** @} */ diff --git a/tests/analog.c b/tests/analog.c index 64e355eb..b5f2beb8 100644 --- a/tests/analog.c +++ b/tests/analog.c @@ -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;