From: Andreas Sandberg Date: Thu, 3 Oct 2024 16:34:43 +0000 (+0100) Subject: strutil: Add a helper to get the precision of a float X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=b6af0adb513db41ef43c05826af9072be8d00024;p=libsigrok.git strutil: Add a helper to get the precision of a float There are a few different instruments where the precision of a measurement can be determined from the number of decimals in the reported floats. Add a helper to determine this. The implemented version of sr_count_digits performs more stringent error checking than the original version in sr_atod_ascii_digits. There are two reasons for this: First, we can no longer rely on atod failing if the provided value is invalid. Second, there are exotic float formats accepted by atod that we don't support and we don't want to be in a situation where atod accepts a value and count digits returns an incorrect value. Signed-off-by: Andreas Sandberg --- diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h index 91bf53b3..fe2ac520 100644 --- a/src/libsigrok-internal.h +++ b/src/libsigrok-internal.h @@ -2004,6 +2004,8 @@ SR_PRIV int sr_atod_ascii(const char *str, double *ret); SR_PRIV int sr_atod_ascii_digits(const char *str, double *ret, int *digits); SR_PRIV int sr_atof_ascii(const char *str, float *ret); +SR_PRIV int sr_count_digits(const char *str, int *digits); + SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len); SR_PRIV void sr_hexdump_free(GString *s); diff --git a/src/strutil.c b/src/strutil.c index 2136bd49..2cc20575 100644 --- a/src/strutil.c +++ b/src/strutil.c @@ -340,59 +340,17 @@ SR_PRIV int sr_atod_ascii(const char *str, double *ret) */ SR_PRIV int sr_atod_ascii_digits(const char *str, double *ret, int *digits) { - const char *p; - int *dig_ref, m_dig, exp; - char c; + int d; double f; - /* - * Convert floating point text to the number value, _and_ get - * the value's precision in the process. Steps taken to do it: - * - Skip leading whitespace. - * - Count the number of decimals after the mantissa's period. - * - Get the exponent's signed value. - * - * This implementation still uses common code for the actual - * conversion, but "violates API layers" by duplicating the - * text scan, to get the number of significant digits. - */ - p = str; - while (*p && isspace(*p)) - p++; - if (*p == '-' || *p == '+') - p++; - m_dig = 0; - exp = 0; - dig_ref = NULL; - while (*p) { - c = *p++; - if (toupper(c) == 'E') { - exp = strtol(p, NULL, 10); - break; - } - if (c == '.') { - m_dig = 0; - dig_ref = &m_dig; - continue; - } - if (isdigit(c)) { - if (dig_ref) - (*dig_ref)++; - continue; - } - /* Need not warn, conversion will fail. */ - break; - } - sr_spew("atod digits: txt \"%s\" -> m %d, e %d -> digits %d", - str, m_dig, exp, m_dig + -exp); - m_dig += -exp; - - if (sr_atod_ascii(str, &f) != SR_OK) + if (sr_count_digits(str, &d) != SR_OK || sr_atod_ascii(str, &f) != SR_OK) return SR_ERR; + if (ret) *ret = f; + if (digits) - *digits = m_dig; + *digits = d; return SR_OK; } @@ -439,6 +397,73 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret) return SR_OK; } +/** + * Get the precision of a floating point number. + * + * @param[in] str The input text to convert. + * @param[out] digits The number of significant decimals. + * + * @returns SR_OK in case of successful. + * @returns SR_ERR when conversion fails. + * + * @private + */ +SR_PRIV int sr_count_digits(const char *str, int *digits) +{ + const char *p = str; + char *exp_end; + int d_int = 0; + int d_dec = 0; + int exp = 0; + + /* Skip leading spaces */ + while ((*p) && isspace(*p)) + p++; + + /* Skip the integer part */ + if ((*p == '-') || (*p == '+')) + p++; + while (isdigit(*p)) { + d_int++; + p++; + } + + /* Count decimal digits. */ + if (*p == '.') { + p++; + while (isdigit(*p)) { + p++; + d_dec++; + } + } + + /* Parse the exponent */ + if (toupper(*p) == 'E') { + p++; + errno = 0; + exp = strtol(p, &exp_end, 10); + if (errno) { + sr_spew("Failed to parse exponent: txt \"%s\", e \"%s\"", + str, p); + return SR_ERR; + } + p = exp_end; + } + + sr_spew("count digits: txt \"%s\" -> d_int %d, d_dec %d, e %d " + "-> digits %d\n", + str, d_int, d_dec, exp, d_dec - exp); + + /* We should have parsed the whole string by now. Return an + * error if not. */ + if ((*p) || (d_dec == 0 && d_int == 0)) { + return SR_ERR; + } + + *digits = d_dec - exp; + return SR_OK; +} + /** * Compose a string with a format string in the buffer pointed to by buf. *