strutil: add method to get an sr_rational from a string

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.
This commit is contained in:
Stefan Brüns 2016-04-23 00:33:45 +02:00 committed by Uwe Hermann
parent 510aa8281f
commit 5ec172d7e9
3 changed files with 122 additions and 0 deletions

View 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 -------------------------------------------------------------*/

View 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.

View 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;