diff --git a/libc/stdlib/strtod.cpp b/libc/stdlib/strtod.cpp index 1f98896f..783ea857 100644 --- a/libc/stdlib/strtod.cpp +++ b/libc/stdlib/strtod.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -25,6 +25,10 @@ #define STRTOF_FLOAT double #define STRTOF strtod #define STRTOF_CHAR char +#define STRTOF_CTYPE_CHAR unsigned char #define STRTOF_L(x) x +#define STRTOF_ISSPACE isspace +#define STRTOF_STRNCASECMP strncasecmp +#define STRTOF_POW pow #include "strtof.cpp" diff --git a/libc/stdlib/strtof.cpp b/libc/stdlib/strtof.cpp index 2e07df53..e31548d7 100644 --- a/libc/stdlib/strtof.cpp +++ b/libc/stdlib/strtof.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -26,74 +26,178 @@ #define STRTOF_FLOAT float #define STRTOF strtof #define STRTOF_CHAR char +#define STRTOF_CTYPE_CHAR unsigned char #define STRTOF_L(x) x +#define STRTOF_ISSPACE isspace +#define STRTOF_STRNCASECMP strncasecmp +#define STRTOF_POW powf #endif +#include +#include +#include #include +#include #include +#include -// TODO: This horribly hacky code is taken from sdlquake's common.c, which is -// licensed under the GPL. Since most Sortix software is GPL-compatible -// this will do for now. It's a temporary measure until I get around to -// writing a real strtod function - most of them are true horrors. +// TODO: This is an imperfect implementation that doesn't try to minimize loss +// and explicitly handle overflow conditions. It behaves well enough on +// simple common inputs to do for now. #if !defined(__is_sortix_kernel) +static inline size_t nan_parameter(const STRTOF_CHAR* str) +{ + size_t index = 0; + if ( str[index++] != STRTOF_L('(') ) + return 0; + // TODO: What exactly does digit and nondigit mean here in POSIX? + if ( !str[index] || str[index] == STRTOF_L(')') ) + return 0; + while ( str[index] && str[index] != STRTOF_L(')') ) + index++; + if ( str[index++] != STRTOF_L(')') ) + return 0; + return index; +} + +static inline bool is_hexadecimal_float(const STRTOF_CHAR* str) +{ + if ( *str++ != '0' ) + return false; + if ( *str != 'x' && *str != 'X' ) + return false; + str++; + if ( *str == '.' ) + str++; + return isxdigit((STRTOF_CTYPE_CHAR) *str); +} + +static inline bool is_exponent(const STRTOF_CHAR* str, + STRTOF_CHAR elc, + STRTOF_CHAR euc) +{ + if ( *str != elc && *str != euc ) + return false; + str++; + if ( *str == '-' || *str == '+' ) + str++; + return STRTOF_L('0') <= *str && *str <= STRTOF_L('9'); +} + +static inline bool parse_digit(const STRTOF_CHAR* str, + int base, + STRTOF_FLOAT* out) +{ + if ( STRTOF_L('0') <= *str && *str <= STRTOF_L('9') ) + return *out = *str - STRTOF_L('0') , true; + if ( base == 16 ) + { + if ( STRTOF_L('a') <= *str && *str <= STRTOF_L('f') ) + return *out = 10 + *str - STRTOF_L('a') , true; + if ( STRTOF_L('A') <= *str && *str <= STRTOF_L('F') ) + return *out = 10 + *str - STRTOF_L('A') , true; + } + return false; +} + extern "C" STRTOF_FLOAT STRTOF(const STRTOF_CHAR* str, STRTOF_CHAR** nptr) { - int sign = *str == STRTOF_L('-') ? (str++, -1) : 1; - STRTOF_FLOAT val = 0.0; + if ( nptr ) + *nptr = (STRTOF_CHAR*) str; - if ( false ) + while ( *str && STRTOF_ISSPACE((STRTOF_CTYPE_CHAR) *str) ) + str++; + + if ( !STRTOF_STRNCASECMP(str, STRTOF_L("INF"), 3) ) { - out: + str += 3; + if ( !STRTOF_STRNCASECMP(str, STRTOF_L("INITY"), 5) ) + str += 5; if ( nptr ) *nptr = (STRTOF_CHAR*) str; - return val * sign; + return INFINITY; } - if ( str[0] == STRTOF_L('0') && - (str[1] == STRTOF_L('x') || str[1] == STRTOF_L('X')) ) + if ( !STRTOF_STRNCASECMP(str, STRTOF_L("NAN"), 3) ) + { + str += 3; + str += nan_parameter(str); + if ( nptr ) + *nptr = (STRTOF_CHAR*) str; + return NAN; + } + + bool negative = *str == STRTOF_L('-'); + if ( *str == STRTOF_L('-') ) + str++; + else if ( *str == STRTOF_L('+') ) + str++; + + int base = 10; + STRTOF_CHAR elc = 'e'; + STRTOF_CHAR euc = 'E'; + + if ( is_hexadecimal_float(str) ) { str += 2; - while ( true ) + base = 16; + elc = 'p'; + euc = 'P'; + } + + bool any_digits = false; + STRTOF_FLOAT result = 0.0; + STRTOF_FLOAT add; + + while ( parse_digit(str, base, &add) ) + { + str++; + result = base * result + add; + any_digits = true; + } + + if ( *str == STRTOF_L('.' ) ) + { + str++; + STRTOF_FLOAT magnitude = 1.0; + while ( parse_digit(str, base, &add) ) { - STRTOF_CHAR c = *str++; - if ( STRTOF_L('0') <= c && c <= STRTOF_L('9') ) - val = val * 16 + c - STRTOF_L('0'); - else if ( STRTOF_L('a') <= c && c <= STRTOF_L('f') ) - val = val * 16 + c - STRTOF_L('a') + 10; - else if ( STRTOF_L('A') <= c && c <= STRTOF_L('F') ) - val = val * 16 + c - STRTOF_L('A') + 10; - else - goto out; + str++; + magnitude /= base; + result += add * magnitude; + any_digits = true; } } - int decimal = -1; - int total = 0; - while ( true ) + if ( !any_digits ) + return errno = EINVAL, 0; + + if ( is_exponent(str, elc, euc) ) { - STRTOF_CHAR c = *str++; - if ( c == STRTOF_L('.') ) + str++; + bool exponent_negative = *str == STRTOF_L('-'); + if ( *str == STRTOF_L('-') ) + str++; + else if ( *str == STRTOF_L('+') ) + str++; + STRTOF_FLOAT exponent = 0.0; + while ( parse_digit(str, 10, &add) ) { - decimal = total; - continue; + str++; + exponent = 10.0 * exponent + add; + any_digits = true; } - if ( c < STRTOF_L('0') || c > STRTOF_L('9') ) - break; - val = val * 10 + c - STRTOF_L('0'); - total++; + if ( exponent_negative ) + exponent = -exponent; + result *= STRTOF_POW(base == 16 ? 2 : base, exponent); } - if ( decimal == -1 ) - goto out; + if ( negative ) + result = -result; - while ( decimal < total ) - { - val /= 10; - total--; - } - - goto out; + if ( nptr ) + *nptr = (STRTOF_CHAR*) str; + return result; } #endif diff --git a/libc/stdlib/strtold.cpp b/libc/stdlib/strtold.cpp index 9ad1da33..e31f0986 100644 --- a/libc/stdlib/strtold.cpp +++ b/libc/stdlib/strtold.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -25,6 +25,10 @@ #define STRTOF_FLOAT long double #define STRTOF strtold #define STRTOF_CHAR char +#define STRTOF_CTYPE_CHAR unsigned char #define STRTOF_L(x) x +#define STRTOF_ISSPACE isspace +#define STRTOF_STRNCASECMP strncasecmp +#define STRTOF_POW pow /* TODO: powl */ #include "strtof.cpp" diff --git a/libc/wchar/wcstod.cpp b/libc/wchar/wcstod.cpp index 9514fb61..4016ffe7 100644 --- a/libc/wchar/wcstod.cpp +++ b/libc/wchar/wcstod.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2014. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -25,6 +25,10 @@ #define STRTOF_FLOAT double #define STRTOF wcstod #define STRTOF_CHAR wchar_t +#define STRTOF_CTYPE_CHAR wint_t #define STRTOF_L(x) L##x +#define STRTOF_ISSPACE iswspace +#define STRTOF_STRNCASECMP wcsncasecmp +#define STRTOF_POW pow #include "../stdlib/strtof.cpp" diff --git a/libc/wchar/wcstof.cpp b/libc/wchar/wcstof.cpp index 450f8fbf..cf5dfca1 100644 --- a/libc/wchar/wcstof.cpp +++ b/libc/wchar/wcstof.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2014. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -25,6 +25,10 @@ #define STRTOF_FLOAT float #define STRTOF wcstof #define STRTOF_CHAR wchar_t +#define STRTOF_CTYPE_CHAR wint_t #define STRTOF_L(x) L##x +#define STRTOF_ISSPACE iswspace +#define STRTOF_STRNCASECMP wcsncasecmp +#define STRTOF_POW powf #include "../stdlib/strtof.cpp" diff --git a/libc/wchar/wcstold.cpp b/libc/wchar/wcstold.cpp index a881ea11..e13682c2 100644 --- a/libc/wchar/wcstold.cpp +++ b/libc/wchar/wcstold.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2014. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -25,6 +25,10 @@ #define STRTOF_FLOAT long double #define STRTOF wcstold #define STRTOF_CHAR wchar_t +#define STRTOF_CTYPE_CHAR wint_t #define STRTOF_L(x) L##x +#define STRTOF_ISSPACE iswspace +#define STRTOF_STRNCASECMP wcsncasecmp +#define STRTOF_POW pow /* TODO: powl */ #include "../stdlib/strtof.cpp"