diff options
author | Thomas Voss <mail@thomasvoss.com> | 2024-06-24 09:05:56 +0200 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2024-06-24 09:05:56 +0200 |
commit | 8810e260f591a780feb251fa5dcaa195b2dcc366 (patch) | |
tree | 8893309ee1d041614cbbce2a66a136c804797a0b /src | |
parent | 398081f8960d34b1a9c28f9d2736f15a80d02c85 (diff) |
Try to handle floating point numbers better
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.c | 21 | ||||
-rw-r--r-- | src/codegen.c | 31 |
2 files changed, 43 insertions, 9 deletions
diff --git a/src/analyzer.c b/src/analyzer.c index a449653..c7cf3a5 100644 --- a/src/analyzer.c +++ b/src/analyzer.c @@ -1,5 +1,7 @@ #include <assert.h> #include <ctype.h> +#include <limits.h> +#include <math.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -17,6 +19,10 @@ #include "symtab.h" #include "types.h" +#define LOG2_10 (3.321928) +#define MP_BITCNT_MAX ((mp_bitcnt_t)-1) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + /* In debug builds we want to actually alloc a new mpq_t so that it’s easier to free memory without doing a double free */ #if DEBUG @@ -259,11 +265,14 @@ analyzeexpr(struct azctx ctx, scope_t *scps, type_t *types, ast_t ast, aux_t aux, lexemes_t toks, idx_t i) { switch (ast.kinds[i]) { - case ASTNUMLIT: + case ASTNUMLIT: { + strview_t sv = toks.strs[ast.lexemes[i]]; types[i].kind = TYPE_NUM; types[i].size = 0; types[i].issigned = true; + types[i].isfloat = memchr(sv.p, '.', sv.len) != NULL; return fwdnode(ast, i); + } case ASTIDENT: { strview_t sv = toks.strs[ast.lexemes[i]]; @@ -403,11 +412,13 @@ constfoldexpr(struct cfctx ctx, mpq_t *folds, scope_t *scps, type_t *types, buf[len] = 0; if (isfloat) { + /* TODO: Is this correct? It seems to work… but I don’t know + if there is a better way to do this; sometimes the + precision is a few bits more than it could be. */ mpf_t x; - /* FIXME: This uses global precision, not thread safe! */ - /* FIXME: This doesn’t try to figure out what the correct - precision is. */ - int ret = mpf_init_set_str(x, buf, 10); + double prec = ceil((sv.len - 1) * LOG2_10); + mpf_init2(x, MIN(MP_BITCNT_MAX, (mp_bitcnt_t)prec)); + int ret = mpf_set_str(x, buf, 10); assert(ret == 0); mpq_set_f(folds[i], x); mpf_clear(x); diff --git a/src/codegen.c b/src/codegen.c index 69f0214..d036b74 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -155,13 +155,36 @@ codegentypedexpr(struct cgctx ctx, idx_t i, type_t type, LLVMValueRef *outv) *outv = LLVMConstIntOfString(type2llvm(ctx, type), buf, 10); return fwdnode(ctx.ast, i); } else if (MPQ_IS_INIT(ctx.folds[i]) /* && type.isfloat */) { - /* FIXME: This is bad and broken */ + char *s, *buf; + size_t len; mpf_t x; - mpf_init(x); + mp_exp_t e; + mp_bitcnt_t prec; + + /* TODO: Is this even correct? */ + switch (type.size) { + case 2: prec = 5; break; + case 4: prec = 8; break; + case 8: prec = 11; break; + case 16: prec = 16; break; + default: __builtin_unreachable(); + } + + mpf_init2(x, prec); mpf_set_q(x, ctx.folds[i]); - char buf[256]; - gmp_snprintf(buf, sizeof(buf), "%Ff", x); + + s = mpf_get_str(NULL, &e, 10, 0, x); + len = strlen(s); + buf = tmpalloc(ctx.s, len + 2, 1); + for (size_t i = 0; i < (size_t)e; i++) + buf[i] = s[i]; + buf[e] = '.'; + for (size_t i = e; i < len; i++) + buf[i + 1] = s[i]; + buf[len + 1] = 0; *outv = LLVMConstRealOfString(type2llvm(ctx, type), buf); + + free(s); mpf_clear(x); return fwdnode(ctx.ast, i); } |