]> granicus.if.org Git - procps-ng/commitdiff
A locale-independent strtod
authorCraig Small <csmall@enc.com.au>
Thu, 3 Mar 2016 10:24:08 +0000 (21:24 +1100)
committerCraig Small <csmall@enc.com.au>
Thu, 3 Mar 2016 10:24:08 +0000 (21:24 +1100)
There is a need in some utilities to have a way of accepting both
types of decimal points "." and ",". The only way seems to be to
rebuild strtod().

This new function will accept "123.456" and "123,456" as 123.456
and considers them the same number. It means we lose thousands
separator, but this is rarely used.

test scripts are added to check the function returns the proper
values. There was simpler predecessor that got stuck on negative
0 or -0.123 which these tests flushed out.

References:

.gitignore
Makefile.am
include/strutils.h
lib/.gitignore
lib/strutils.c
lib/test_strtod_nol.c [new file with mode: 0644]

index bebea7dd225d82c9b1ed7544841c44d7b5e40ee5..4dd11a224b6a4d8e9f83481b6fe8a33ed9323bce 100644 (file)
@@ -50,6 +50,7 @@ slabtop
 snice
 stamp-h1
 sysctl
+test-driver
 tload
 top/Makefile
 top/Makefile.in
index fe1f12f9b726a2d2359f0f6d89362cffd82eef8c..a7216c74e8e465d30e98ac92eb0f77c5163ecb77 100644 (file)
@@ -217,12 +217,15 @@ ps_pscommand_SOURCES =  \
        ps/stacktrace.c \
        lib/fileutils.c
 
+TESTS = lib/test_strtod_nol
+
 # lib/test_* binaries
 noinst_PROGRAMS = \
        lib/test_strutils \
        lib/test_fileutils \
        lib/test_nsutils \
-       lib/test_process
+       lib/test_process \
+       $(TESTS)
 
 lib_test_strutils_SOURCES = lib/test_strutils.c lib/strutils.c
 lib_test_strutils_LDADD =
@@ -233,6 +236,9 @@ lib_test_nsutils_LDADD =
 lib_test_process_SOURCES = lib/test_process.c
 lib_test_process_LDADD =
 
+lib_test_strtod_nol_SOURCES = lib/test_strtod_nol.c lib/strutils.c
+lib_test_strtod_nol_LDADD =
+
 if EXAMPLE_FILES
 sysconf_DATA = sysctl.conf
 endif
index 9df33c21cab9ea004c746c2a1ad478a07320df47..85a6192002f5f129d435cca62b687525214647a8 100644 (file)
@@ -7,5 +7,6 @@
 
 extern long strtol_or_err(const char *str, const char *errmesg);
 extern double strtod_or_err(const char *str, const char *errmesg);
+double strtod_nol_or_err(char *str, const char *errmesg);
 
 #endif
index 7043aff7d7dfdcbf5a6eb9b730ccf6d389424410..e54c8ee2b9043a796b589fc216cd7fd636238bc4 100644 (file)
@@ -1,5 +1,7 @@
 .dirstamp
+*.trs
 test_fileutils
 test_process
 test_strutils
 test_nsutils
+test_strtod_nol
index 0fd3cf715d26fd3a19928d65afdb196abfd66252..e5245db06b4094a572e0030220f21273c6abeb53 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include <stdlib.h>
+#include <ctype.h>
 
 #include "c.h"
 #include "strutils.h"
@@ -60,3 +61,63 @@ double strtod_or_err(const char *str, const char *errmesg)
        error(EXIT_FAILURE, errno, "%s: '%s'", errmesg, str);
        return 0;
 }
+
+/*
+ * Covert a string into a double in a non-locale aware way.
+ * This means the decimal point can be either . or ,
+ * Also means you cannot use the other for thousands separator
+ *
+ * Exits on failure like its other _or_err cousins
+ */
+double strtod_nol_or_err(char *str, const char *errmesg)
+{
+    double num;
+    const char *cp, *radix;
+    double mult;
+    int negative = 0;
+
+    if (str != NULL && *str != '\0') {
+        num = 0.0;
+        cp = str;
+        /* strip leading spaces */
+        while (isspace(*cp))
+            cp++;
+
+        /* get sign */
+        if (*cp == '-') {
+            negative = 1;
+            cp++;
+        } else if (*cp == '+')
+            cp++;
+
+        /* find radix */
+        radix = cp;
+        mult=0.1;
+        while(isdigit(*radix)) {
+            radix++;
+            mult *= 10;
+        }
+        while(isdigit(*cp)) {
+            num += (*cp - '0') * mult;
+            mult /= 10;
+            cp++;
+        }
+        /* got the integers */
+        if (*cp == '\0')
+            return (negative?-num:num);
+        if (*cp != '.' && *cp != ',')
+            error(EXIT_FAILURE, EINVAL, "%s: '%s'", errmesg, str);
+
+        cp++;
+        mult = 0.1;
+        while(isdigit(*cp)) {
+            num += (*cp - '0') * mult;
+            mult /= 10;
+            cp++;
+        }
+        if (*cp == '\0')
+            return (negative?-num:num);
+    }
+    error(EXIT_FAILURE, errno, "%s: '%s'", errmesg, str);
+    return 0;
+}
diff --git a/lib/test_strtod_nol.c b/lib/test_strtod_nol.c
new file mode 100644 (file)
index 0000000..0be798c
--- /dev/null
@@ -0,0 +1,45 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "strutils.h"
+
+struct strtod_tests {
+    char *string;
+    double result;
+};
+
+struct strtod_tests tests[] = {
+    {"123",     123.0},
+    {"-123",    -123.0},
+    {"12.34",   12.34},
+    {"-12.34",  -12.34},
+    {".34",     0.34},
+    {"-.34",    -0.34},
+    {"12,34",   12.34},
+    {"-12,34",  -12.34},
+    {",34",     0.34},
+    {"-,34",    -0.34},
+    {"0",       0.0},
+    {".0",      0.0},
+    {"0.0",     0.0},
+    {NULL, 0.0}
+};
+
+
+
+int main(int argc, char *argv[])
+{
+    int i;
+    double val;
+
+    for(i=0; tests[i].string != NULL; i++) {
+        if(strtod_nol_or_err(tests[i].string, "Cannot parse number") !=
+           tests[i].result) {
+            fprintf(stderr, "FAIL: strtod_nol_or_err(\"%s\") != %f\n",
+                    tests[i].string, tests[i].result);
+            return EXIT_FAILURE;
+        }
+        //fprintf(stderr, "PASS: strtod_nol for %s\n", tests[i].string);
+    }
+    return EXIT_SUCCESS;
+}