summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-02-15 12:16:21 -0500
committerpommicket <pommicket@gmail.com>2022-02-15 12:16:21 -0500
commit1ea9d85e3fdf3e90ceb46938c40bc4b1cfc09d89 (patch)
tree2bfaec112031ec46241f022ad7098000d7b837f1
parentf7c3154b8a0108ddeecdbd5afc3ac2c99124aab6 (diff)
strtod
also fixed comparing strings
-rw-r--r--05/codegen.b20
-rw-r--r--05/main.c8
-rw-r--r--05/parse.b1
-rw-r--r--05/stdc_common.h189
4 files changed, 192 insertions, 26 deletions
diff --git a/05/codegen.b b/05/codegen.b
index 8e06318..b89ab51 100644
--- a/05/codegen.b
+++ b/05/codegen.b
@@ -1515,6 +1515,8 @@ function comparison_type
argument expr
local type1
local type2
+ local t1
+ local t2
expr += 8
type1 = expr + 4
@@ -1523,17 +1525,19 @@ function comparison_type
type2 = expr + 4
type2 = *4type2
- type1 += types
- type1 = *1type1
- type2 += types
- type2 = *1type2
+ t1 = types + type1
+ t1 = *1t1
+ t2 = types + type2
+ t2 = *1t2
; do float comparisons as double comparisons to make things simpler
- if type1 == TYPE_FLOAT goto return_type_double
- if type2 == TYPE_FLOAT goto return_type_double
+ if t1 == TYPE_FLOAT goto return_type_double
+ if t2 == TYPE_FLOAT goto return_type_double
- if type1 == TYPE_POINTER goto return_type_unsigned_long
- if type2 == TYPE_POINTER goto return_type_unsigned_long
+ if t1 == TYPE_POINTER goto return_type_unsigned_long
+ if t2 == TYPE_POINTER goto return_type_unsigned_long
+ if t1 == TYPE_ARRAY goto return_type_unsigned_long ; type1 decays to pointer
+ if t2 == TYPE_ARRAY goto return_type_unsigned_long ; type2 decays to pointer
return expr_binary_type_usual_conversions(statement, type1, type2)
; is this comparison expression a comparison between unsigned integers or floats?
diff --git a/05/main.c b/05/main.c
index 6b7aac7..7b5789c 100644
--- a/05/main.c
+++ b/05/main.c
@@ -3,11 +3,11 @@
#include <string.h>
int main(void) {
- char s[] = " -0XAh.\n";
+ char *s = "1.35984534e135-e12hello";
char *end;
- errno = 0;
- printf("%ld\n", strtol(s, &end, 0));
- printf("%d:%s",errno,end);
+ _Float f = _powers_of_10[-307];
+ printf("%.15g\n",strtod(s, &end));
+ printf("%s\n",end);
return 0;
}
diff --git a/05/parse.b b/05/parse.b
index 9cc0f39..18258ab 100644
--- a/05/parse.b
+++ b/05/parse.b
@@ -2775,7 +2775,6 @@ function parse_expression
if c == EXPRESSION_LT goto type_int
if c == EXPRESSION_GT goto type_int
if c == EXPRESSION_COMMA goto type_binary_right
- if c == EXPRESSION_EQ goto type_binary_left
if c == EXPRESSION_ASSIGN goto type_binary_left
if c == EXPRESSION_ASSIGN_ADD goto type_binary_left
if c == EXPRESSION_ASSIGN_SUB goto type_binary_left
diff --git a/05/stdc_common.h b/05/stdc_common.h
index d9e66f1..22c5a42 100644
--- a/05/stdc_common.h
+++ b/05/stdc_common.h
@@ -1,11 +1,6 @@
#ifndef _STDC_COMMON_H
#define _STDC_COMMON_H
-#ifdef _STDLIB_DEBUG
-int printf(char *);
-#endif
-
-
#define signed
#define volatile
#define register
@@ -105,6 +100,24 @@ void _Exit(int status) {
return __syscall(60, status, 0, 0, 0, 0, 0);
}
+int kill(int pid, int sig) {
+ return __syscall(62, pid, sig, 0, 0, 0, 0);
+}
+
+int getpid(void) {
+ return __syscall(39, 0, 0, 0, 0, 0, 0);
+}
+
+#define SIGABRT 6
+#define SIGFPE 8
+#define SIGILL 4
+#define SIGINT 2
+#define SIGSEGV 11
+#define SIGTERM 15
+void abort(void) {
+ kill(getpid(), SIGABRT);
+}
+
typedef long time_t;
struct timespec {
@@ -126,8 +139,29 @@ int access(const char *pathname, int mode) {
return __syscall(21, pathname, mode, 0, 0, 0, 0);
}
+typedef struct {
+ int fd;
+ unsigned char eof;
+ unsigned char err;
+} FILE;
int errno;
+int printf(char *, ...);
+int fprintf(FILE *, char *, ...); // needed now for assert()
+
+FILE _stdin = {0}, *stdin;
+FILE _stdout = {1}, *stdout;
+FILE _stderr = {2}, *stderr;
+
+#ifdef NDEBUG
+#define assert(x) ((void)0)
+#else
+int __assert_failed(const char *file, int line, const char *expr) {
+ fprintf(stderr, "Assertion failed at %s:%d: %s\n", file, line, expr);
+ abort();
+}
+#define assert(x) (void)((x) || __assert_failed(__FILE__, __LINE__, #x))
+#endif
#define EIO 5
#define EDOM 33
@@ -223,7 +257,7 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) {
value = newvalue;
++nptr;
}
- *endptr = nptr;
+ if (endptr) *endptr = nptr;
if (overflow) {
errno = ERANGE;
return ULONG_MAX;
@@ -255,22 +289,151 @@ long strtol(const char *nptr, char **endptr, int base) {
}
}
+long _strtol_clamped(const char *nptr, char **endptr, int base, int min, int max) {
+ long l = strtol(nptr, endptr, base);
+ if (l < min) return min;
+ if (l > max) return max;
+ return l;
+}
+
+#define _NPOW10 310
+#define _INFINITY 1e1000
+// non-negative floating-point number with more precision than a double
+// its value is equal to fraction * 2^exponent
typedef struct {
- int fd;
- unsigned char eof;
- unsigned char err;
-} FILE;
+ unsigned long fraction;
+ int exponent;
+} _Float;
+
+// ensure that f->fraction >= 2^64 / 2
+static void _normalize_float(_Float *f) {
+ if (!f->fraction) return;
+ while (f->fraction < 0x8000000000000000) {
+ f->exponent -= 1;
+ f->fraction <<= 1;
+ }
+}
+
+static double _Float_to_double(_Float f) {
+ unsigned long dbl_fraction;
+ int dbl_exponent;
+ unsigned long dbl_value;
+ if (f.fraction == 0) return 0;
+ _normalize_float(&f);
+ f.fraction &= 0x7fffffffffffffff; // remove the "1." in 1.01101110111... to get 63-bit significand
+ dbl_fraction = (f.fraction + 0x3ff) >> 11;
+ dbl_exponent = f.exponent + 63;
+ if (dbl_exponent < -1022) return 0;
+ if (dbl_exponent > 1023) return _INFINITY;
+ dbl_exponent += 1023;
+ dbl_value = (unsigned long)dbl_exponent << 52 | dbl_fraction;
+ return *(double *)&dbl_value;
+}
+
+static _Float _powers_of_10_dat[2*_NPOW10+1];
+static _Float *_powers_of_10;
+static _Float _Float_ZERO = {0, 1};
+static _Float _Float_INFINITY = {0x8000000000000000, 100000};
+
+
+_Float _int_pow10(int x) {
+ if (x <= -_NPOW10) return _Float_ZERO;
+ if (x >= _NPOW10) return _Float_INFINITY;
+ return _powers_of_10[x];
+}
+
+double strtod(const char *nptr, char **endptr) {
+ const char *flt, *dot, *p, *number_end;
+ double sign = 1;
+ int exponent = 0;
+ while (isspace(*nptr)) ++nptr;
+
+ flt = nptr; // start of float
+ if (*flt == '+') ++flt;
+ else if (*flt == '-') sign = -1, ++flt;
+
+ if (*flt != '.' && (*flt < '0' || *flt > '9')) {
+ // this isn't a float
+ *endptr = nptr;
+ return 0;
+ }
+
+ // find the decimal point, if any
+ dot = flt;
+ while (*dot >= '0' && *dot <= '9') ++dot;
+
+ nptr = dot + (*dot == '.');
+ // skip digits after the dot
+ while (*nptr >= '0' && *nptr <= '9') ++nptr;
+ number_end = nptr;
+
+ if (*nptr == 'e') {
+ ++nptr;
+ exponent = 1;
+ if (*nptr == '+') ++nptr;
+ else if (*nptr == '-') ++nptr, exponent = -1;
+ exponent *= _strtol_clamped(nptr, &nptr, 10, -10000, 10000); // use _strtol_clamped to prevent problems with -LONG_MIN
+ }
+
+ // construct the value using the Kahan summation algorithm (https://en.wikipedia.org/wiki/Kahan_summation_algorithm)
+ double sum = 0;
+ double c = 0;
+ for (p = flt; p < number_end; ++p) {
+ if (*p == '.') continue;
+ int n = *p - '0';
+ assert(n >= 0 && n <= 9);
+ int pow10 = dot - p;
+ pow10 -= pow10 > 0;
+ pow10 += exponent;
+ _Float f_val = _int_pow10(pow10);
+ f_val.fraction >>= 4;
+ f_val.exponent += 4;
+ f_val.fraction *= n;
+ double value = _Float_to_double(f_val);
+ if (value == _INFINITY || sum == _INFINITY) {
+ sum = _INFINITY;
+ break;
+ }
+ double y = value - c;
+ double t = sum + y;
+ c = (t - sum) - y;
+ sum = t;
+ }
+
+ if (endptr) *endptr = nptr;
+ return sum * sign;
+}
-FILE _stdin = {0}, *stdin;
-FILE _stdout = {1}, *stdout;
-FILE _stderr = {2}, *stderr;
int main();
int _main(int argc, char **argv) {
+ int i;
+ _Float p = {1, 0};
+
stdin = &_stdin;
stdout = &_stdout;
stderr = &_stderr;
+
+ // initialize powers of 10
+ _powers_of_10 = _powers_of_10_dat + _NPOW10;
+ for (i = 0; i < _NPOW10; ++i) {
+ _normalize_float(&p);
+ _powers_of_10[i] = p;
+ p.exponent += 4;
+ p.fraction >>= 4;
+ p.fraction *= 10;
+ }
+
+ p.fraction = 1;
+ p.exponent = 0;
+ for (i = 0; i > -_NPOW10; --i) {
+ _normalize_float(&p);
+ _powers_of_10[i] = p;
+ p.fraction /= 5;
+ p.exponent -= 1;
+ }
+
return main(argc, argv);
}