]> sigrok.org Git - libsigrok.git/commitdiff
strutil: Add a helper to get the precision of a float
authorAndreas Sandberg <redacted>
Thu, 3 Oct 2024 16:34:43 +0000 (17:34 +0100)
committerSoeren Apel <redacted>
Mon, 7 Oct 2024 12:46:40 +0000 (14:46 +0200)
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 <redacted>
src/libsigrok-internal.h
src/strutil.c

index 91bf53b3a84c4f810d0206d25eb45c00f989798d..fe2ac52085d2099ece95a069e7393330d8848a5e 100644 (file)
@@ -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);
 
index 2136bd4936c2fde5bf303bbf491e938af5d4acb1..2cc205758131e82ff2ce3660a1efdb976bebbe41 100644 (file)
@@ -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.
  *