]> sigrok.org Git - libsigrok.git/commitdiff
strutil: add method to get an sr_rational from a string
authorStefan Brüns <redacted>
Fri, 22 Apr 2016 22:33:45 +0000 (00:33 +0200)
committerUwe Hermann <redacted>
Mon, 16 May 2016 21:35:10 +0000 (23:35 +0200)
The method accepts strings with numbers in scientific or normal notation,
e.g. -1.25 or 3.37e-6. The numeric range is limited by the sr_rational
range, i.e +-9.2e18, resolution is ~19 digits.

include/libsigrok/proto.h
src/strutil.c
tests/strutil.c

index caa8407e23a19ede70a311b6231c3555236cb47d..f2dc8d4953c12246c208733418285b7f61cc795e 100644 (file)
@@ -226,6 +226,7 @@ SR_API uint64_t sr_parse_timestring(const char *timestring);
 SR_API gboolean sr_parse_boolstring(const char *boolstring);
 SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q);
 SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q);
+SR_API int sr_parse_rational(const char *str, struct sr_rational *ret);
 
 /*--- version.c -------------------------------------------------------------*/
 
index d817e642d8f1cb9dfb44292c9ea0d8acf11f73af..3d7283b3e846e87338e7af9f97406dc266ccdee6 100644 (file)
@@ -211,6 +211,77 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret)
        return SR_OK;
 }
 
+/**
+ * Convert a string representation of a numeric value to a @sr_rational. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid number. The function sets errno according to the details of the
+ * failure. This version ignores the locale.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to sr_rational where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.5.0
+ */
+SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
+{
+       char *endptr = NULL;
+       int64_t integral;
+       int64_t fractional = 0;
+       int64_t denominator = 1;
+       int32_t fractional_len = 0;
+       int32_t exponent = 0;
+
+       errno = 0;
+       integral = g_ascii_strtoll(str, &endptr, 10);
+
+       if (errno)
+               return SR_ERR;
+
+       if (*endptr == '.') {
+               const char* start = endptr + 1;
+               fractional = g_ascii_strtoll(start, &endptr, 10);
+               if (errno)
+                       return SR_ERR;
+               fractional_len = endptr - start;
+       }
+
+       if ((*endptr == 'E') || (*endptr == 'e')) {
+               exponent = g_ascii_strtoll(endptr + 1, &endptr, 10);
+               if (errno)
+                       return SR_ERR;
+       }
+
+       if (*endptr != '\0')
+               return SR_ERR;
+
+       for (int i = 0; i < fractional_len; i++)
+               integral *= 10;
+       exponent -= fractional_len;
+
+       if (integral >= 0)
+               integral += fractional;
+       else
+               integral -= fractional;
+
+       while (exponent > 0) {
+               integral *= 10;
+               exponent--;
+       }
+
+       while (exponent < 0) {
+               denominator *= 10;
+               exponent++;
+       }
+
+       ret->p = integral;
+       ret->q = denominator;
+
+       return SR_OK;
+}
+
 /**
  * Convert a numeric value value to its "natural" string representation
  * in SI units.
index 53417fba60f02d178ff0ce4188ba407cb0f5e7a7..ed7bb120b77dc7e3973dd87c0d21bac170b146ba 100644 (file)
@@ -34,6 +34,18 @@ static void test_samplerate(uint64_t samplerate, const char *expected)
        g_free(s);
 }
 
+static void test_rational(const char *input, struct sr_rational expected)
+{
+       int ret;
+       struct sr_rational rational;
+
+       ret = sr_parse_rational(input, &rational);
+       fail_unless(ret == SR_OK);
+       fail_unless((expected.p == rational.p) && (expected.q == rational.q),
+                   "Invalid result for '%s': %ld/%ld'.",
+                   input, rational.p, rational.q);
+}
+
 /*
  * Check various inputs for sr_samplerate_string():
  *
@@ -155,6 +167,41 @@ START_TEST(test_ghz)
 }
 END_TEST
 
+START_TEST(test_integral)
+{
+       test_rational("1", (struct sr_rational){1, 1});
+       test_rational("2", (struct sr_rational){2, 1});
+       test_rational("10", (struct sr_rational){10, 1});
+       test_rational("-255", (struct sr_rational){-255, 1});
+}
+END_TEST
+
+START_TEST(test_fractional)
+{
+       test_rational("0.1", (struct sr_rational){1, 10});
+       test_rational("1.0", (struct sr_rational){10, 10});
+       test_rational("1.2", (struct sr_rational){12, 10});
+       test_rational("12.34", (struct sr_rational){1234, 100});
+       test_rational("-12.34", (struct sr_rational){-1234, 100});
+       test_rational("10.00", (struct sr_rational){1000, 100});
+}
+END_TEST
+
+START_TEST(test_exponent)
+{
+       test_rational("1e0", (struct sr_rational){1, 1});
+       test_rational("1E0", (struct sr_rational){1, 1});
+       test_rational("1E1", (struct sr_rational){10, 1});
+       test_rational("1e-1", (struct sr_rational){1, 10});
+       test_rational("-1.234e-0", (struct sr_rational){-1234, 1000});
+       test_rational("-1.234e3", (struct sr_rational){-1234, 1});
+       test_rational("-1.234e-3", (struct sr_rational){-1234, 1000000});
+       test_rational("0.001e3", (struct sr_rational){1, 1});
+       test_rational("0.001e0", (struct sr_rational){1, 1000});
+       test_rational("0.001e-3", (struct sr_rational){1, 1000000});
+}
+END_TEST
+
 Suite *suite_strutil(void)
 {
        Suite *s;
@@ -168,6 +215,9 @@ Suite *suite_strutil(void)
        tcase_add_test(tc, test_khz);
        tcase_add_test(tc, test_mhz);
        tcase_add_test(tc, test_ghz);
+       tcase_add_test(tc, test_integral);
+       tcase_add_test(tc, test_fractional);
+       tcase_add_test(tc, test_exponent);
        suite_add_tcase(s, tc);
 
        return s;