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:
parent
510aa8281f
commit
5ec172d7e9
|
@ -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 -------------------------------------------------------------*/
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue