From e18e9a988e42e59c1468c59190ceaced15d01d0f Mon Sep 17 00:00:00 2001 From: bumbread Date: Sun, 5 Jun 2022 14:13:27 +1100 Subject: [PATCH 1/2] Complete implementation of strto* and ato* functions --- code/errno.c | 4 ++ code/stdlib/conv.c | 166 ++++++++++++++++++++++++++++++--------------- inc/errno.h | 2 +- test/test.c | 43 ++++++++++-- 4 files changed, 155 insertions(+), 60 deletions(-) create mode 100644 code/errno.c diff --git a/code/errno.c b/code/errno.c new file mode 100644 index 0000000..cdb2993 --- /dev/null +++ b/code/errno.c @@ -0,0 +1,4 @@ + +#include + +int errno; diff --git a/code/stdlib/conv.c b/code/stdlib/conv.c index f40392d..aebe2ff 100644 --- a/code/stdlib/conv.c +++ b/code/stdlib/conv.c @@ -9,6 +9,14 @@ // TODO: i made a function that parses longs and i'm fucken // tired. someone make float parsing :kekw: +// Call me weak if you want but I'm actually too lazy to type +// them out every time, also they take up a lot of horiz space. +typedef long long int intll; +typedef long int intl; +typedef unsigned long long int intull; +typedef unsigned long int intul; +typedef unsigned int intu; + #define inrange(start, c, end) ((start) <= (c) && (c) <= (end)) static bool isbase(int c, int base) { @@ -43,92 +51,144 @@ static long todigitl(int c) { return val; } -long -strtol(const char *restrict nptr, char **restrict endptr, int inbase) { - if(!inrange(0, inbase, 36)) { - *endptr = NULL; - return 0; +static intull strto_generic( + const char *restrict nptr, + char **restrict endptr, + int inbase, + intl *coefptr, + intull int_max +) { + const char *restrict str = nptr; + intull value = 0; + int digits_read = 0; + bool is_signed = (coefptr != NULL); + // Find max{abs(int)}. Signed integers have negative, + // whose absolute value is 1 bigger than int_max. + intull int_abs_max = int_max; + if(is_signed) { + ++int_abs_max; } + if(!inrange(0, inbase, 36)) { + goto finish; + } + intull base = (intull)inbase; // Skip space on the beginning - while(isspace(*nptr)) { - ++nptr; + while(isspace(*str)) { + ++str; } // Parse sign - long coef = 1; - if(*nptr == '-') { - coef = -1; - ++nptr; + intl coef = 1; + if(is_signed) { + if(*str == '-') { + coef = -1; + ++str; + } } - if(*nptr == '+') { - ++nptr; + if(*str == '+') { + ++str; } - unsigned long base = (unsigned long)inbase; - unsigned long value = 0; // See if we need to parse base in C-like format - if(*nptr == '0' && *(nptr+1) == 'x') { - ++nptr; + // then set the base accordingly + if(*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) { + ++str; if(base == 16 || base == 0) { - ++nptr; + ++str; base = 16; } else { - value = 0; - goto end; + goto finish; } } - else if(*nptr == '0') { - ++nptr; + else if(*str == '0') { + ++str; + ++digits_read; if(base == 8 || base == 0) { base = 8; } } - while(isbase(*nptr, (int)base)) { - unsigned long digit = (unsigned long)todigitl(*nptr); - if(value > (ULONG_MAX - digit)/base) { - errno = ERANGE; - value = 0; - goto end; + // Parse the string of digits in the given base. If the value + // exceeds abs(int_min) we exit with range error. + while(isbase(*str, (int)base)) { + intull digit = (intull)todigitl(*str); + if(value > (int_abs_max - digit)/base) { + goto error_out_of_range; } value = base*value + digit; - ++nptr; + ++str; + ++digits_read; } - unsigned long max_modulo = (unsigned long)LONG_MAX+1; - if(value > max_modulo) { - errno = ERANGE; + // We only allow the modulo of value equal to abs(int_min) if it is + // preceeded by the minus sign. + if(is_signed) { + if(value == int_abs_max && coef != -1) { + goto error_out_of_range; + } + } + goto finish; +error_out_of_range: + // Skip the remainder of the subject string + while(isbase(*str, (int)base)) { + ++str; + } + errno = ERANGE; + value = int_max; + goto finish; +finish: + // If no conversion is performed we return the value of 0 and *endptr + // is set to the nptr. + bool conv_performed = (digits_read > 0); + if(!conv_performed) { value = 0; - goto end; } - if(value == max_modulo) { - if(coef == 1) { - errno = ERANGE; - value = 0; - goto end; + if(endptr != NULL) { + if(!conv_performed) { + *endptr = (char *)nptr; } else { - value = LONG_MIN; - coef = 1; + *endptr = (char *)str; } } -end: - if(endptr != NULL) { - *endptr = (char *)nptr; + *coefptr = coef; + return value; +} + +intl strtol(const char *restrict nptr, char **restrict endptr, int base) { + intull int_max = (intull)LONG_MAX; + intl coef; + intull modulo = strto_generic(nptr, endptr, base, &coef, int_max); + intl value; + if(modulo == int_max) { + value = LONG_MIN; } - return coef*(long)value; + else { + value = coef * (intl)modulo; + } + return value; } -long long -strtoll(const char *restrict nptr, char **restrict endptr, int base) { - return 0; +intll strtoll(const char *restrict nptr, char **restrict endptr, int base) { + intull int_max = (intull)LLONG_MAX; + intl coef; + intull modulo = strto_generic(nptr, endptr, base, &coef, int_max); + intll value; + if(modulo == int_max) { + value = LLONG_MIN; + } + else { + value = (intll)coef * (intll)modulo; + } + return value; } -unsigned long -strtoul(const char *restrict nptr, char **restrict endptr, int base) { - return 0; +intul strtoul(const char *restrict nptr, char **restrict endptr, int base) { + intull int_max = (intull)ULONG_MAX; + intull value = strto_generic(nptr, endptr, base, NULL, int_max); + return (intul)value; } -unsigned long long -strtoull(const char *restrict nptr, char **restrict endptr, int base) { - return 0; +intull strtoull(const char *restrict nptr, char **restrict endptr, int base) { + intull int_max = (intull)ULLONG_MAX; + return strto_generic(nptr, endptr, base, NULL, int_max); } double atof(const char *nptr) { diff --git a/inc/errno.h b/inc/errno.h index a1783de..50f3328 100644 --- a/inc/errno.h +++ b/inc/errno.h @@ -5,4 +5,4 @@ #define ERANGE 3 // TODO: make it thread-local -int errno; +extern int errno; diff --git a/test/test.c b/test/test.c index d895fce..6e69e5f 100644 --- a/test/test.c +++ b/test/test.c @@ -1,13 +1,44 @@ #include #include +#include +#include +#include +#include int main(int argc, char** argv) { - /*for (int i = 0; i < argv; i++) { - printf("[%d] = %s\n", argv[i]); - }*/ - - for (char c = 'a'; c != 'z'; ++c) { - assert(isupper(toupper(c))); + const char *p = "10 200000000000000000000000000000 30 -40 junk"; + // printf("Parsing '%s':\n", p); + + for (;;) + { + // errno can be set to any non-zero value by a library function call + // regardless of whether there was an error, so it needs to be cleared + // in order to check the error set by strtol + errno = 0; + char *end; + const long i = strtol(p, &end, 10); + if (p == end) + break; + + const bool range_error = errno == ERANGE; + // printf("Extracted '%.*s', strtol returned %ld.", (int)(end-p), p, i); + p = end; + + // if (range_error) + // printf(" Range error occurred."); + + // putchar('\n'); } + + // printf("Unextracted leftover: '%s'\n\n", p); + + // parsing without error handling + long v1 = strtol("1010", NULL, 2); + long v2 = strtol("12", NULL, 8); + long v3 = strtol("A", NULL, 16); + long v4 = strtol("junk", NULL, 36); + long v5 = strtol("012", NULL, 0); + long v6 = strtol("0xA", NULL, 0); + long v7 = strtol("junk", NULL, 0); return 0; } From 7b6bea0ff15c2fc2d33dfc265152adadcd16c55e Mon Sep 17 00:00:00 2001 From: bumbread Date: Mon, 6 Jun 2022 09:02:54 +1100 Subject: [PATCH 2/2] strtod, strtof, strtold --- code/stdlib/conv.c | 156 +++++++++++++++++++++++++++++++++++++++++---- inc/math.h | 17 +++-- test/test.c | 57 ++++++----------- 3 files changed, 172 insertions(+), 58 deletions(-) diff --git a/code/stdlib/conv.c b/code/stdlib/conv.c index aebe2ff..88a7540 100644 --- a/code/stdlib/conv.c +++ b/code/stdlib/conv.c @@ -4,10 +4,10 @@ #include #include #include +#include -// TODO: strto*: locale-based parsing for integers -// TODO: i made a function that parses longs and i'm fucken -// tired. someone make float parsing :kekw: +// TODO: strto*: locale-based parsing +// TODO: correctly rounded base 16 floats parsing // Call me weak if you want but I'm actually too lazy to type // them out every time, also they take up a lot of horiz space. @@ -36,8 +36,20 @@ static bool isbase(int c, int base) { return val < base; } +static bool +strprefix_i(char const *restrict str, char const *restrict prefix) +{ + while(*prefix != 0) { + if(*str == 0) break; + if(toupper(*str) != toupper(*prefix)) return false; + ++prefix; + ++str; + } + return true; +} + // Called only when isbase(c, base) for some base in range -static long todigitl(int c) { +static long todigit(int c) { int val; if(isdigit(c)) { val = c-'0'; @@ -51,7 +63,7 @@ static long todigitl(int c) { return val; } -static intull strto_generic( +static intull strtoi_generic( const char *restrict nptr, char **restrict endptr, int inbase, @@ -89,7 +101,7 @@ static intull strto_generic( } // See if we need to parse base in C-like format // then set the base accordingly - if(*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) { + if(strprefix_i(str, "0X")) { ++str; if(base == 16 || base == 0) { ++str; @@ -109,7 +121,7 @@ static intull strto_generic( // Parse the string of digits in the given base. If the value // exceeds abs(int_min) we exit with range error. while(isbase(*str, (int)base)) { - intull digit = (intull)todigitl(*str); + intull digit = (intull)todigit(*str); if(value > (int_abs_max - digit)/base) { goto error_out_of_range; } @@ -152,10 +164,126 @@ finish: return value; } +static double strtod_generic( + const char *restrict nptr, + char **restrict endptr +) { + const char *restrict str = nptr; + bool conv_performed = false; + double value = 0.0; + double coef = 1.0; + // Skip space on the beginning + while(isspace(*str)) { + ++str; + } + // Check for inf and nan + if(strprefix_i(str, "INF")) { + str += sizeof "INF"-1; + value = HUGE_VAL; + conv_performed = true; + goto finish; + } + if(strprefix_i(str, "INFINITY")) { + str += sizeof "INFINITY"-1; + value = HUGE_VAL; + conv_performed = true; + goto finish; + } + if(strprefix_i(str, "NAN")) { + str += sizeof "NAN"-1; + value = NAN; + conv_performed = true; + if(*str == '(') { + while(*str != ')') { + ++str; + } + ++str; + } + goto finish; + } + // Parse C float + if(*str == '+') { + ++str; + } + else if(*str == '-') { + coef = -1.; + ++str; + } + int base = 10; + if(strprefix_i(str, "0X")) { + str += sizeof "0X"-1; + base = 16; + } + // Parse the whole part + while(isbase(*str, base)) { + long digit = todigit(*str); + value = 10.0*value + (double)digit; + ++str; + } + if(*str != '.') { + value = 0.0; + goto finish; + } + ++str; + // Parse the fractional part + long exp = 1; + while(isbase(*str, base)) { + long digit = todigit(*str); + double fract = (double)digit; + long cexp = exp; + while(cexp-- != 0) { + fract /= (double)base; + } + value += fract; + ++exp; + ++str; + } + // Parse the exponent + if((base == 10 && strprefix_i(str, "E")) + || (base == 16 && strprefix_i(str, "P"))) + { + ++str; + long exp = 0; + long exp_coef = 1; + if(*str == '+') { + ++str; + } + else if(*str == '-') { + exp_coef = -1; + ++str; + } + while(isdigit(*str)) { + exp = 10*exp + (long)(*str-'0'); + ++str; + } + if(exp_coef == 1) { + while(exp--!=0) value = value * base; + } + else if(exp_coef == -1) { + while(exp--!=0) value = value / base; + } + } + if(!isfinite(value)) { + errno = ERANGE; + value = coef*HUGE_VAL; + } + conv_performed = true; +finish: + if(endptr != NULL) { + if(conv_performed) { + *endptr = (char *)str; + } + else { + *endptr = (char *)nptr; + } + } + return coef*value; +} + intl strtol(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)LONG_MAX; intl coef; - intull modulo = strto_generic(nptr, endptr, base, &coef, int_max); + intull modulo = strtoi_generic(nptr, endptr, base, &coef, int_max); intl value; if(modulo == int_max) { value = LONG_MIN; @@ -169,7 +297,7 @@ intl strtol(const char *restrict nptr, char **restrict endptr, int base) { intll strtoll(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)LLONG_MAX; intl coef; - intull modulo = strto_generic(nptr, endptr, base, &coef, int_max); + intull modulo = strtoi_generic(nptr, endptr, base, &coef, int_max); intll value; if(modulo == int_max) { value = LLONG_MIN; @@ -182,13 +310,13 @@ intll strtoll(const char *restrict nptr, char **restrict endptr, int base) { intul strtoul(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)ULONG_MAX; - intull value = strto_generic(nptr, endptr, base, NULL, int_max); + intull value = strtoi_generic(nptr, endptr, base, NULL, int_max); return (intul)value; } intull strtoull(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)ULLONG_MAX; - return strto_generic(nptr, endptr, base, NULL, int_max); + return strtoi_generic(nptr, endptr, base, NULL, int_max); } double atof(const char *nptr) { @@ -208,13 +336,13 @@ long long int atoll(const char *nptr) { } double strtod(const char *restrict nptr, char **restrict endptr) { - return 0; + return strtod_generic(nptr, endptr); } float strtof(const char *restrict nptr, char **restrict endptr) { - return 0; + return (float)strtod_generic(nptr, endptr); } long double strtold(const char *restrict nptr, char **restrict endptr) { - return 0; + return (long double)strtod_generic(nptr, endptr); } diff --git a/inc/math.h b/inc/math.h index 98557be..1b398fa 100644 --- a/inc/math.h +++ b/inc/math.h @@ -21,11 +21,11 @@ typedef double double_t; #define FP_INFINITE 1 #define FP_NAN 2 -FP_INFINITE -FP_NAN -FP_NORMAL -FP_SUBNORMAL -FP_ZERO +//FP_INFINITE +//FP_NAN +//FP_NORMAL +//FP_SUBNORMAL +//FP_ZERO #define FP_FAST_FMA 1 #define FP_FAST_FMAF 1 #define FP_FAST_FMAL 1 @@ -39,8 +39,11 @@ FP_ZERO // TODO: implement this #define fpclassify(x) 0 -#define isfinite(x) 0 -#define isinf(x) 0 + +// HACK: If its fine just remove the comment +#define isfinite(x) ((double)(x) != HUGE_VAL && (double)(x) != -HUGE_VAL) +#define isinf(x) (!(isinf(x))) + #define isnan(x) 0 #define isnormal(x) 0 #define signbit(x) 0 diff --git a/test/test.c b/test/test.c index 6e69e5f..99d9653 100644 --- a/test/test.c +++ b/test/test.c @@ -1,44 +1,27 @@ -#include -#include -#include -#include -#include +#include #include +#include + +int main(void) +{ + double v0 = strtod("0X1.BC70A3D70A3D7P+6", NULL); -int main(int argc, char** argv) { - const char *p = "10 200000000000000000000000000000 30 -40 junk"; - // printf("Parsing '%s':\n", p); - - for (;;) + // parsing with error handling + const char *p = "111.11 -2.22 Nan nan(2) inF 0X1.BC70A3D70A3D7P+6 1.18973e+4932zzz"; + + char *end; + for (double f = strtod(p, &end); p != end; f = strtod(p, &end)) { - // errno can be set to any non-zero value by a library function call - // regardless of whether there was an error, so it needs to be cleared - // in order to check the error set by strtol - errno = 0; - char *end; - const long i = strtol(p, &end, 10); - if (p == end) - break; - - const bool range_error = errno == ERANGE; - // printf("Extracted '%.*s', strtol returned %ld.", (int)(end-p), p, i); + // printf("'%.*s' -> ", (int)(end-p), p); p = end; - - // if (range_error) - // printf(" Range error occurred."); - - // putchar('\n'); + if (errno == ERANGE){ + // printf("range error, got "); + errno = 0; + } + // printf("%f\n", f); } - // printf("Unextracted leftover: '%s'\n\n", p); - // parsing without error handling - long v1 = strtol("1010", NULL, 2); - long v2 = strtol("12", NULL, 8); - long v3 = strtol("A", NULL, 16); - long v4 = strtol("junk", NULL, 36); - long v5 = strtol("012", NULL, 0); - long v6 = strtol("0xA", NULL, 0); - long v7 = strtol("junk", NULL, 0); - return 0; -} + double v1 = strtod(" -0.0000000123junk", NULL); + double v2 = strtod("junk", NULL); +} \ No newline at end of file