From d96ff92461bd050081eae676af08c520efa9e4da Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 8 Aug 2023 15:17:25 +0200 Subject: [PATCH] libc: Add borrowed strtod implementation --- libc/CMakeLists.txt | 1 + libc/include/stdlib.h | 6 +- libc/src/stdlib.cpp | 5 ++ libc/src/strtod.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 libc/src/strtod.cpp diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 0dd645e4..a9daa5d9 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -24,6 +24,7 @@ set(SOURCES src/signal.cpp src/termios.cpp src/utime.cpp + src/strtod.cpp src/sys/stat.cpp src/sys/mman.cpp src/sys/wait.cpp diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index bbbc07d3..9fa130f0 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -84,9 +84,11 @@ extern "C" /* Parse a decimal integer from a string. */ long long atoll(const char* s); - double atof(const char*); + /* Parse a floating-point number from a string. */ + double atof(const char* str); - double strtod(const char*, char**); + /* Parse a floating-point number from a string. */ + double strtod(const char* str, char** endptr); /* Parse an integer of the specified base from a string, storing the first non-number character in endptr if * nonnull. */ diff --git a/libc/src/stdlib.cpp b/libc/src/stdlib.cpp index 32150fc7..d58074f5 100644 --- a/libc/src/stdlib.cpp +++ b/libc/src/stdlib.cpp @@ -297,4 +297,9 @@ extern "C" if (errno != EEXIST) return -1; } } + + double atof(const char* str) + { + return strtod(str, nullptr); + } } diff --git a/libc/src/strtod.cpp b/libc/src/strtod.cpp new file mode 100644 index 00000000..74b77cc1 --- /dev/null +++ b/libc/src/strtod.cpp @@ -0,0 +1,141 @@ +/* vi:set ts=8 sts=4 sw=4: */ +/* + * strtod implementation. + * author: Yasuhiro Matsumoto + * license: public domain + * + * source from https://gist.github.com/mattn/1890186 + */ + +#include +#include +#include +#include +#include + +static const char* skipwhite(const char* q) +{ + const char* p = q; + while (isspace(*p)) ++p; + return p; +} + +extern "C" +{ + double strtod(const char* str, char** end) + { + double d = 0.0; + int sign; + int n = 0; + const char *p, *a; + + a = p = str; + p = skipwhite(p); + + /* decimal part */ + sign = 1; + if (*p == '-') + { + sign = -1; + ++p; + } + else if (*p == '+') + ++p; + if (isdigit(*p)) + { + d = (double)(*p++ - '0'); + while (*p && isdigit(*p)) + { + d = d * 10.0 + (double)(*p - '0'); + ++p; + ++n; + } + a = p; + } + else if (*p != '.') + goto done; + d *= sign; + + /* fraction part */ + if (*p == '.') + { + double f = 0.0; + double base = 0.1; + ++p; + + if (isdigit(*p)) + { + while (*p && isdigit(*p)) + { + f += base * (*p - '0'); + base /= 10.0; + ++p; + ++n; + } + } + d += f * sign; + a = p; + } + + /* exponential part */ + if ((*p == 'E') || (*p == 'e')) + { + int e = 0; + ++p; + + sign = 1; + if (*p == '-') + { + sign = -1; + ++p; + } + else if (*p == '+') + ++p; + + if (isdigit(*p)) + { + while (*p == '0') ++p; + if (*p == '\0') --p; + e = (int)(*p++ - '0'); + while (*p && isdigit(*p)) + { + e = e * 10 + (int)(*p - '0'); + ++p; + } + e *= sign; + } + else if (!isdigit(*(a - 1))) + { + a = str; + goto done; + } + else if (*p == 0) + goto done; + + if (d == 2.2250738585072011 && e == -308) + { + d = 0.0; + a = p; + errno = ERANGE; + goto done; + } + if (d == 2.2250738585072012 && e <= -308) + { + d *= 1.0e-308; + a = p; + goto done; + } + d *= __builtin_powi(10.0, e); + a = p; + } + else if (p > str && !isdigit(*(p - 1))) + { + a = str; + goto done; + } + + done: + if (end) *end = const_cast(a); + return d; + } +}