From 8810e260f591a780feb251fa5dcaa195b2dcc366 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Mon, 24 Jun 2024 09:05:56 +0200 Subject: Try to handle floating point numbers better --- make.c | 2 +- src/analyzer.c | 21 ++++++++++++++++----- src/codegen.c | 31 +++++++++++++++++++++++++++---- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/make.c b/make.c index c57362d..f96a039 100644 --- a/make.c +++ b/make.c @@ -300,7 +300,7 @@ ld(void) if (!rflag && !Sflag) strspushl(&cmd, "-fsanitize=address,undefined"); llvmquery(&cmd, LLVM_LDFLAGS | LLVM_LIBS); - strspushl(&cmd, "-o", TARGET); + strspushl(&cmd, "-lm", "-o", TARGET); assert(glob("src/*.o", 0, globerr, &g) == 0); for (size_t i = 0; i < g.gl_pathc; i++) { 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 #include +#include +#include #include #include #include @@ -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); } -- cgit v1.2.3