From 09bd4a9a8693485fa87a5f62af90540ca8ce9d47 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Aug 2017 00:33:05 -0400 Subject: [PATCH] compile-time f32, f64 operations are now correctly lossy previously we used the bigfloat abstraction to do all compile-time float math. but runtime code and comptime code are supposed to get the same result. so now if you add a f32 to a f32 at compile time it does it with f32 math instead of the bigfloat. float literals still get the bigfloat math. closes #424 --- src/all_types.hpp | 3 + src/analyze.cpp | 87 +++++- src/ast_render.cpp | 4 +- src/bigfloat.cpp | 61 ++--- src/bigfloat.hpp | 12 +- src/bigint.cpp | 21 +- src/bigint.hpp | 3 +- src/codegen.cpp | 29 +- src/ir.cpp | 652 +++++++++++++++++++++++++++++++++++++++++--- src/quadmath.hpp | 19 ++ src/tokenizer.cpp | 4 +- std/math/atan2.zig | 2 +- std/math/sin.zig | 1 + test/cases/eval.zig | 20 ++ 14 files changed, 808 insertions(+), 110 deletions(-) create mode 100644 src/quadmath.hpp diff --git a/src/all_types.hpp b/src/all_types.hpp index 9f898f9982..40ef91cff9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -225,6 +225,9 @@ struct ConstExprValue { // populated if special == ConstValSpecialStatic BigInt x_bigint; BigFloat x_bigfloat; + float x_f32; + double x_f64; + __float128 x_f128; bool x_bool; ConstFn x_fn; ConstBoundFnValue x_bound_fn; diff --git a/src/analyze.cpp b/src/analyze.cpp index a59c77a1c5..05ec20aced 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -13,6 +13,7 @@ #include "ir_print.hpp" #include "os.hpp" #include "parser.hpp" +#include "quadmath.hpp" #include "zig_llvm.hpp" static const size_t default_backward_branch_quota = 1000; @@ -3273,8 +3274,35 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return result; } case TypeTableEntryIdFloat: + switch (const_val->type->data.floating.bit_count) { + case 32: + { + uint32_t result; + memcpy(&result, &const_val->data.x_f32, 4); + return result ^ 4084870010; + } + case 64: + { + uint32_t ints[2]; + memcpy(&ints[0], &const_val->data.x_f64, 8); + return ints[0] ^ ints[1] ^ 0x22ed43c6; + } + case 128: + { + uint32_t ints[4]; + memcpy(&ints[0], &const_val->data.x_f128, 16); + return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xb5ffef27; + } + default: + zig_unreachable(); + } case TypeTableEntryIdNumLitFloat: - return (uint32_t)(const_val->data.x_bigfloat.value * (uint32_t)UINT32_MAX); + { + __float128 f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); + uint32_t ints[4]; + memcpy(&ints[0], &f128, 16); + return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb; + } case TypeTableEntryIdArgTuple: return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 + (uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768; @@ -3575,7 +3603,25 @@ ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) { const_val->special = ConstValSpecialStatic; const_val->type = type; - bigfloat_init_float(&const_val->data.x_bigfloat, value); + if (type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_64(&const_val->data.x_bigfloat, value); + } else if (type->id == TypeTableEntryIdFloat) { + switch (type->data.floating.bit_count) { + case 32: + const_val->data.x_f32 = value; + break; + case 64: + const_val->data.x_f64 = value; + break; + case 128: + // if we need this, we should add a function that accepts a __float128 param + zig_unreachable(); + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } } ConstExprValue *create_const_float(TypeTableEntry *type, double value) { @@ -3816,6 +3862,17 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdBool: return a->data.x_bool == b->data.x_bool; case TypeTableEntryIdFloat: + assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count); + switch (a->type->data.floating.bit_count) { + case 32: + return a->data.x_f32 == b->data.x_f32; + case 64: + return a->data.x_f64 == b->data.x_f64; + case 128: + return a->data.x_f128 == b->data.x_f128; + default: + zig_unreachable(); + } case TypeTableEntryIdNumLitFloat: return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; case TypeTableEntryIdInt: @@ -3984,12 +4041,32 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "{}"); return; case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdFloat: - bigfloat_write_buf(buf, &const_val->data.x_bigfloat); + bigfloat_append_buf(buf, &const_val->data.x_bigfloat); return; + case TypeTableEntryIdFloat: + switch (type_entry->data.floating.bit_count) { + case 32: + buf_appendf(buf, "%f", const_val->data.x_f32); + return; + case 64: + buf_appendf(buf, "%f", const_val->data.x_f64); + return; + case 128: + { + const size_t extra_len = 100; + size_t old_len = buf_len(buf); + buf_resize(buf, old_len + extra_len); + int len = quadmath_snprintf(buf_ptr(buf) + old_len, extra_len, "%Qf", const_val->data.x_f128); + assert(len > 0); + buf_resize(buf, old_len + len); + return; + } + default: + zig_unreachable(); + } case TypeTableEntryIdNumLitInt: case TypeTableEntryIdInt: - bigint_write_buf(buf, &const_val->data.x_bigint, 10); + bigint_append_buf(buf, &const_val->data.x_bigint, 10); return; case TypeTableEntryIdMetaType: buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 5613347f7d..23a83fadd0 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -540,7 +540,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { Buf rendered_buf = BUF_INIT; buf_resize(&rendered_buf, 0); - bigfloat_write_buf(&rendered_buf, node->data.float_literal.bigfloat); + bigfloat_append_buf(&rendered_buf, node->data.float_literal.bigfloat); fprintf(ar->f, "%s", buf_ptr(&rendered_buf)); } break; @@ -548,7 +548,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { Buf rendered_buf = BUF_INIT; buf_resize(&rendered_buf, 0); - bigint_write_buf(&rendered_buf, node->data.int_literal.bigint, 10); + bigint_append_buf(&rendered_buf, node->data.int_literal.bigint, 10); fprintf(ar->f, "%s", buf_ptr(&rendered_buf)); } break; diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp index c1bfcbbb97..33cff544cc 100644 --- a/src/bigfloat.cpp +++ b/src/bigfloat.cpp @@ -8,19 +8,19 @@ #include "bigfloat.hpp" #include "bigint.hpp" #include "buffer.hpp" +#include "quadmath.hpp" #include #include -extern "C" { - __float128 fmodq(__float128 a, __float128 b); - __float128 ceilq(__float128 a); - __float128 floorq(__float128 a); - __float128 strtoflt128 (const char *s, char **sp); - int quadmath_snprintf (char *s, size_t size, const char *format, ...); +void bigfloat_init_128(BigFloat *dest, __float128 x) { + dest->value = x; } +void bigfloat_init_32(BigFloat *dest, float x) { + dest->value = x; +} -void bigfloat_init_float(BigFloat *dest, __float128 x) { +void bigfloat_init_64(BigFloat *dest, double x) { dest->value = x; } @@ -104,11 +104,13 @@ void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { dest->value = fmodq(fmodq(op1->value, op2->value) + op2->value, op2->value); } -void bigfloat_write_buf(Buf *buf, const BigFloat *op) { - buf_resize(buf, 256); - int len = quadmath_snprintf(buf_ptr(buf), buf_len(buf), "%Qf", op->value); +void bigfloat_append_buf(Buf *buf, const BigFloat *op) { + const size_t extra_len = 100; + size_t old_len = buf_len(buf); + buf_resize(buf, old_len + extra_len); + int len = quadmath_snprintf(buf_ptr(buf) + old_len, extra_len, "%Qf", op->value); assert(len > 0); - buf_resize(buf, len); + buf_resize(buf, old_len + len); } Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) { @@ -121,42 +123,15 @@ Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) { } } -// TODO this is wrong when compiler running on big endian systems. caught by tests -void bigfloat_write_ieee597(const BigFloat *op, uint8_t *buf, size_t bit_count, bool is_big_endian) { - if (bit_count == 32) { - float f32 = op->value; - memcpy(buf, &f32, 4); - } else if (bit_count == 64) { - double f64 = op->value; - memcpy(buf, &f64, 8); - } else if (bit_count == 128) { - __float128 f128 = op->value; - memcpy(buf, &f128, 16); - } else { - zig_unreachable(); - } +float bigfloat_to_f32(const BigFloat *bigfloat) { + return (float)bigfloat->value; } -// TODO this is wrong when compiler running on big endian systems. caught by tests -void bigfloat_read_ieee597(BigFloat *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian) { - if (bit_count == 32) { - float f32; - memcpy(&f32, buf, 4); - dest->value = f32; - } else if (bit_count == 64) { - double f64; - memcpy(&f64, buf, 8); - dest->value = f64; - } else if (bit_count == 128) { - __float128 f128; - memcpy(&f128, buf, 16); - dest->value = f128; - } else { - zig_unreachable(); - } +double bigfloat_to_f64(const BigFloat *bigfloat) { + return (double)bigfloat->value; } -double bigfloat_to_double(const BigFloat *bigfloat) { +__float128 bigfloat_to_f128(const BigFloat *bigfloat) { return bigfloat->value; } diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp index 0ec33d4a0d..1923bade0c 100644 --- a/src/bigfloat.hpp +++ b/src/bigfloat.hpp @@ -19,12 +19,16 @@ struct BigFloat { struct Buf; -void bigfloat_init_float(BigFloat *dest, __float128 x); +void bigfloat_init_32(BigFloat *dest, float x); +void bigfloat_init_64(BigFloat *dest, double x); +void bigfloat_init_128(BigFloat *dest, __float128 x); void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x); void bigfloat_init_bigint(BigFloat *dest, const BigInt *op); int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len); -double bigfloat_to_double(const BigFloat *bigfloat); +float bigfloat_to_f32(const BigFloat *bigfloat); +double bigfloat_to_f64(const BigFloat *bigfloat); +__float128 bigfloat_to_f128(const BigFloat *bigfloat); void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_negate(BigFloat *dest, const BigFloat *op); @@ -35,10 +39,8 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2 void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); -void bigfloat_write_buf(Buf *buf, const BigFloat *op); +void bigfloat_append_buf(Buf *buf, const BigFloat *op); Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); -void bigfloat_write_ieee597(const BigFloat *op, uint8_t *buf, size_t bit_count, bool is_big_endian); -void bigfloat_read_ieee597(BigFloat *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian); // convenience functions diff --git a/src/bigint.cpp b/src/bigint.cpp index 16d630ebe3..63393f6358 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -141,6 +141,21 @@ void bigint_init_unsigned(BigInt *dest, uint64_t x) { dest->is_negative = false; } +void bigint_init_u128(BigInt *dest, unsigned __int128 x) { + uint64_t low = (uint64_t)(x & UINT64_MAX); + uint64_t high = (uint64_t)(x >> 64); + + if (high == 0) { + return bigint_init_unsigned(dest, low); + } + + dest->digit_count = 2; + dest->data.digits = allocate_nonzero(2); + dest->data.digits[0] = low; + dest->data.digits[1] = high; + dest->is_negative = false; +} + void bigint_init_signed(BigInt *dest, int64_t x) { if (x >= 0) { return bigint_init_unsigned(dest, x); @@ -167,9 +182,9 @@ void bigint_init_bigint(BigInt *dest, const BigInt *src) { void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { if (op->value >= 0) { - bigint_init_unsigned(dest, op->value); + bigint_init_u128(dest, (unsigned __int128)(op->value)); } else { - bigint_init_unsigned(dest, -op->value); + bigint_init_u128(dest, (unsigned __int128)(-op->value)); dest->is_negative = true; } } @@ -1023,7 +1038,7 @@ Cmp bigint_cmp(const BigInt *op1, const BigInt *op2) { } } -void bigint_write_buf(Buf *buf, const BigInt *op, uint64_t base) { +void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) { if (op->digit_count == 0) { buf_append_char(buf, '0'); return; diff --git a/src/bigint.hpp b/src/bigint.hpp index fb1b62fb76..eeb70a4d35 100644 --- a/src/bigint.hpp +++ b/src/bigint.hpp @@ -30,6 +30,7 @@ enum Cmp { }; void bigint_init_unsigned(BigInt *dest, uint64_t x); +void bigint_init_u128(BigInt *dest, unsigned __int128 x); void bigint_init_signed(BigInt *dest, int64_t x); void bigint_init_bigint(BigInt *dest, const BigInt *src); void bigint_init_bigfloat(BigInt *dest, const BigFloat *op); @@ -76,7 +77,7 @@ void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_s Cmp bigint_cmp(const BigInt *op1, const BigInt *op2); -void bigint_write_buf(Buf *buf, const BigInt *op, uint64_t base); +void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base); size_t bigint_ctz(const BigInt *bi, size_t bit_count); size_t bigint_clz(const BigInt *bi, size_t bit_count); diff --git a/src/codegen.cpp b/src/codegen.cpp index db934b6b88..847821e94c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1243,10 +1243,6 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { } } -static LLVMValueRef bigfloat_to_llvm_const(LLVMTypeRef type_ref, BigFloat *bigfloat) { - return LLVMConstReal(type_ref, bigfloat_to_double(bigfloat)); -} - static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_math, LLVMValueRef val1, LLVMValueRef val2, TypeTableEntry *type_entry, DivKind div_kind) @@ -3455,7 +3451,23 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref, const_val->data.x_pure_err->value, false); case TypeTableEntryIdFloat: - return bigfloat_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigfloat); + switch (type_entry->data.floating.bit_count) { + case 32: + return LLVMConstReal(type_entry->type_ref, const_val->data.x_f32); + case 64: + return LLVMConstReal(type_entry->type_ref, const_val->data.x_f64); + case 128: + { + // TODO make sure this is correct on big endian targets too + uint8_t buf[16]; + memcpy(buf, &const_val->data.x_f128, 16); + LLVMValueRef as_int = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, + (uint64_t*)buf); + return LLVMConstBitCast(as_int, type_entry->type_ref); + } + default: + zig_unreachable(); + } case TypeTableEntryIdBool: if (const_val->data.x_bool) { return LLVMConstAllOnes(LLVMInt1Type()); @@ -3937,8 +3949,11 @@ static void do_code_gen(CodeGen *g) { // Generate debug info for it but that's it. ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); - TypeTableEntry *var_type = g->builtin_types.entry_f64; - LLVMValueRef init_val = bigfloat_to_llvm_const(var_type->type_ref, &const_val->data.x_bigfloat); + TypeTableEntry *var_type = g->builtin_types.entry_f128; + ConstExprValue coerced_value; + coerced_value.type = var_type; + coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); + LLVMValueRef init_val = gen_const_val(g, &coerced_value); gen_global_var(g, var, init_val, var_type); continue; } diff --git a/src/ir.cpp b/src/ir.cpp index 4c06fe8c34..563f87c304 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12,6 +12,7 @@ #include "ir_print.hpp" #include "os.hpp" #include "parseh.hpp" +#include "quadmath.hpp" #include "range_set.hpp" struct IrExecContext { @@ -6272,6 +6273,546 @@ static bool const_val_fits_in_num_lit(ConstExprValue *const_val, TypeTableEntry (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdNumLitInt))); } +static bool float_has_fraction(ConstExprValue *const_val) { + if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + return bigfloat_has_fraction(&const_val->data.x_bigfloat); + } else if (const_val->type->id == TypeTableEntryIdFloat) { + switch (const_val->type->data.floating.bit_count) { + case 32: + return floorf(const_val->data.x_f32) != const_val->data.x_f32; + case 64: + return floor(const_val->data.x_f64) != const_val->data.x_f64; + case 128: + return floorq(const_val->data.x_f128) != const_val->data.x_f128; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_append_buf(Buf *buf, ConstExprValue *const_val) { + if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_append_buf(buf, &const_val->data.x_bigfloat); + } else if (const_val->type->id == TypeTableEntryIdFloat) { + switch (const_val->type->data.floating.bit_count) { + case 32: + buf_appendf(buf, "%f", const_val->data.x_f32); + break; + case 64: + buf_appendf(buf, "%f", const_val->data.x_f64); + break; + case 128: + { + const size_t extra_len = 100; + size_t old_len = buf_len(buf); + buf_resize(buf, old_len + extra_len); + int len = quadmath_snprintf(buf_ptr(buf) + old_len, extra_len, "%Qf", const_val->data.x_f128); + assert(len > 0); + buf_resize(buf, old_len + len); + break; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { + if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); + } else if (const_val->type->id == TypeTableEntryIdFloat) { + switch (const_val->type->data.floating.bit_count) { + case 32: + if (const_val->data.x_f32 >= 0) { + bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); + } else { + bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32)); + bigint->is_negative = true; + } + break; + case 64: + if (const_val->data.x_f64 >= 0) { + bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64)); + } else { + bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64)); + bigint->is_negative = true; + } + break; + case 128: + if (const_val->data.x_f128 >= 0) { + bigint_init_u128(bigint, (unsigned __int128)(const_val->data.x_f128)); + } else { + bigint_init_u128(bigint, (unsigned __int128)(-const_val->data.x_f128)); + bigint->is_negative = true; + } + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { + if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); + } else if (dest_val->type->id == TypeTableEntryIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 32: + dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); + break; + case 64: + dest_val->data.x_f64 = bigfloat_to_f64(bigfloat); + break; + case 128: + dest_val->data.x_f128 = bigfloat_to_f128(bigfloat); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f32(ConstExprValue *dest_val, float x) { + if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_32(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == TypeTableEntryIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 32: + dest_val->data.x_f32 = x; + break; + case 64: + dest_val->data.x_f64 = x; + break; + case 128: + dest_val->data.x_f128 = x; + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f64(ConstExprValue *dest_val, double x) { + if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_64(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == TypeTableEntryIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 32: + dest_val->data.x_f32 = x; + break; + case 64: + dest_val->data.x_f64 = x; + break; + case 128: + dest_val->data.x_f128 = x; + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f128(ConstExprValue *dest_val, __float128 x) { + if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_128(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == TypeTableEntryIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 32: + dest_val->data.x_f32 = x; + break; + case 64: + dest_val->data.x_f64 = x; + break; + case 128: + dest_val->data.x_f128 = x; + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) { + if (src_val->type->id == TypeTableEntryIdNumLitFloat) { + float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); + } else if (src_val->type->id == TypeTableEntryIdFloat) { + switch (src_val->type->data.floating.bit_count) { + case 32: + float_init_f32(dest_val, src_val->data.x_f32); + break; + case 64: + float_init_f64(dest_val, src_val->data.x_f64); + break; + case 128: + float_init_f128(dest_val, src_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + if (op1->data.x_f32 > op2->data.x_f32) { + return CmpGT; + } else if (op1->data.x_f32 < op2->data.x_f32) { + return CmpLT; + } else { + return CmpEQ; + } + case 64: + if (op1->data.x_f64 > op2->data.x_f64) { + return CmpGT; + } else if (op1->data.x_f64 < op2->data.x_f64) { + return CmpLT; + } else { + return CmpEQ; + } + case 128: + if (op1->data.x_f128 > op2->data.x_f128) { + return CmpGT; + } else if (op1->data.x_f128 < op2->data.x_f128) { + return CmpLT; + } else { + return CmpEQ; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static Cmp float_cmp_zero(ConstExprValue *op) { + if (op->type->id == TypeTableEntryIdNumLitFloat) { + return bigfloat_cmp_zero(&op->data.x_bigfloat); + } else if (op->type->id == TypeTableEntryIdFloat) { + switch (op->type->data.floating.bit_count) { + case 32: + if (op->data.x_f32 < 0.0) { + return CmpLT; + } else if (op->data.x_f32 > 0.0) { + return CmpGT; + } else { + return CmpEQ; + } + case 64: + if (op->data.x_f64 < 0.0) { + return CmpLT; + } else if (op->data.x_f64 > 0.0) { + return CmpGT; + } else { + return CmpEQ; + } + case 128: + if (op->data.x_f128 < 0.0) { + return CmpLT; + } else if (op->data.x_f128 > 0.0) { + return CmpGT; + } else { + return CmpEQ; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64; + return; + case 128: + out_val->data.x_f128 = op1->data.x_f128 + op2->data.x_f128; + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64; + return; + case 128: + out_val->data.x_f128 = op1->data.x_f128 - op2->data.x_f128; + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64; + return; + case 128: + out_val->data.x_f128 = op1->data.x_f128 * op2->data.x_f128; + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; + return; + case 128: + out_val->data.x_f128 = op1->data.x_f128 / op2->data.x_f128; + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; + if (out_val->data.x_f32 >= 0.0) { + out_val->data.x_f32 = floorf(out_val->data.x_f32); + } else { + out_val->data.x_f32 = ceilf(out_val->data.x_f32); + } + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; + if (out_val->data.x_f64 >= 0.0) { + out_val->data.x_f64 = floor(out_val->data.x_f64); + } else { + out_val->data.x_f64 = ceil(out_val->data.x_f64); + } + return; + case 128: + out_val->data.x_f128 = op1->data.x_f128 / op2->data.x_f128; + if (out_val->data.x_f128 >= 0.0) { + out_val->data.x_f128 = floorq(out_val->data.x_f128); + } else { + out_val->data.x_f128 = ceilq(out_val->data.x_f128); + } + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64); + return; + case 128: + out_val->data.x_f128 = floorq(op1->data.x_f128 / op2->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64); + return; + case 128: + out_val->data.x_f128 = fmodq(op1->data.x_f128, op2->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == TypeTableEntryIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); + return; + case 128: + out_val->data.x_f128 = fmodq(fmodq(op1->data.x_f128, op2->data.x_f128) + op2->data.x_f128, op2->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { + out_val->type = op->type; + if (op->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); + } else if (op->type->id == TypeTableEntryIdFloat) { + switch (op->type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = -op->data.x_f32; + return; + case 64: + out_val->data.x_f64 = -op->data.x_f64; + return; + case 128: + out_val->data.x_f128 = -op->data.x_f128; + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { + if (op->type->id == TypeTableEntryIdFloat) { + switch (op->type->data.floating.bit_count) { + case 32: + memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian + return; + case 64: + memcpy(buf, &op->data.x_f64, 8); // TODO wrong when compiler is big endian + return; + case 128: + memcpy(buf, &op->data.x_f128, 16); // TODO wrong when compiler is big endian + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) { + if (val->type->id == TypeTableEntryIdFloat) { + switch (val->type->data.floating.bit_count) { + case 32: + memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian + return; + case 64: + memcpy(&val->data.x_f64, buf, 8); // TODO wrong when compiler is big endian + return; + case 128: + memcpy(&val->data.x_f128, buf, 16); // TODO wrong when compiler is big endian + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type, bool explicit_cast) { @@ -6314,9 +6855,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc if (explicit_cast && (other_type->id == TypeTableEntryIdInt || other_type->id == TypeTableEntryIdNumLitInt) && const_val_is_float) { - if (bigfloat_has_fraction(&const_val->data.x_bigfloat)) { + if (float_has_fraction(const_val)) { Buf *val_buf = buf_alloc(); - bigfloat_write_buf(val_buf, &const_val->data.x_bigfloat); + float_append_buf(val_buf, const_val); ir_add_error(ira, instruction, buf_sprintf("fractional component prevents float value %s from being casted to type '%s'", @@ -6324,11 +6865,11 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc buf_ptr(&other_type->name))); return false; } else { - BigInt bigint; - bigint_init_bigfloat(&bigint, &const_val->data.x_bigfloat); if (other_type->id == TypeTableEntryIdNumLitInt) { return true; } else { + BigInt bigint; + float_init_bigint(&bigint, const_val); if (bigint_fits_in_bits(&bigint, other_type->data.integral.bit_count, other_type->data.integral.is_signed)) { @@ -6342,10 +6883,10 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc Buf *val_buf = buf_alloc(); if (const_val_is_float) { num_lit_str = "float"; - bigfloat_write_buf(val_buf, &const_val->data.x_bigfloat); + float_append_buf(val_buf, const_val); } else { num_lit_str = "integer"; - bigint_write_buf(val_buf, &const_val->data.x_bigint, 10); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); } ir_add_error(ira, instruction, @@ -6767,7 +7308,20 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, } case CastOpNumLitToConcrete: if (other_val->type->id == TypeTableEntryIdNumLitFloat) { - bigfloat_init_bigfloat(&const_val->data.x_bigfloat, &other_val->data.x_bigfloat); + assert(new_type->id == TypeTableEntryIdFloat); + switch (new_type->data.floating.bit_count) { + case 32: + const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); + break; + case 64: + const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat); + break; + case 128: + const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat); + break; + default: + zig_unreachable(); + } } else if (other_val->type->id == TypeTableEntryIdNumLitInt) { bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); } else { @@ -6780,11 +7334,29 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, // can't do it break; case CastOpIntToFloat: - bigfloat_init_bigint(&const_val->data.x_bigfloat, &other_val->data.x_bigint); - const_val->special = ConstValSpecialStatic; - break; + { + assert(new_type->id == TypeTableEntryIdFloat); + + BigFloat bigfloat; + bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); + switch (new_type->data.floating.bit_count) { + case 32: + const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); + break; + case 64: + const_val->data.x_f64 = bigfloat_to_f64(&bigfloat); + break; + case 128: + const_val->data.x_f128 = bigfloat_to_f128(&bigfloat); + break; + default: + zig_unreachable(); + } + const_val->special = ConstValSpecialStatic; + break; + } case CastOpFloatToInt: - bigint_init_bigfloat(&const_val->data.x_bigint, &other_val->data.x_bigfloat); + float_init_bigint(&const_val->data.x_bigint, other_val); const_val->special = ConstValSpecialStatic; break; case CastOpBoolToInt: @@ -7387,12 +7959,12 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction } IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); + result->value.type = wanted_type; if (wanted_type->id == TypeTableEntryIdInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); } else { - bigfloat_init_bigfloat(&result->value.data.x_bigfloat, &val->data.x_bigfloat); + float_init_float(&result->value, val); } - result->value.type = wanted_type; return result; } @@ -7415,7 +7987,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count); if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) { Buf *val_buf = buf_alloc(); - bigint_write_buf(val_buf, &val->data.x_bigint, 10); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, source_instr, buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields", buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count)); @@ -7444,7 +8016,7 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); if (wanted_type->id == TypeTableEntryIdNumLitFloat) { - bigfloat_init_bigfloat(&result->value.data.x_bigfloat, &val->data.x_bigfloat); + float_init_float(&result->value, val); } else if (wanted_type->id == TypeTableEntryIdNumLitInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); } else { @@ -7469,7 +8041,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc bigint_init_unsigned(&err_count, ira->codegen->error_decls.length); if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { Buf *val_buf = buf_alloc(); - bigint_write_buf(val_buf, &val->data.x_bigint, 10); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, source_instr, buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); return ira->codegen->invalid_instruction; @@ -8304,7 +8876,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if ((value_is_comptime(op1_val) && value_is_comptime(op2_val)) || resolved_type->id == TypeTableEntryIdVoid) { bool answer; if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) { - Cmp cmp_result = bigfloat_cmp(&op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + Cmp cmp_result = float_cmp(op1_val, op2_val); answer = resolve_cmp_op_id(op_id, cmp_result); } else if (resolved_type->id == TypeTableEntryIdNumLitInt || resolved_type->id == TypeTableEntryIdInt) { Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); @@ -8379,7 +8951,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, { is_int = false; is_float = true; - op2_zcmp = bigfloat_cmp_zero(&op2_val->data.x_bigfloat); + op2_zcmp = float_cmp_zero(op2_val); } else { zig_unreachable(); } @@ -8447,7 +9019,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, if (is_int) { bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_add(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_add(out_val, op1_val, op2_val); } break; case IrBinOpAddWrap: @@ -8459,7 +9031,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, if (is_int) { bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_sub(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_sub(out_val, op1_val, op2_val); } break; case IrBinOpSubWrap: @@ -8471,7 +9043,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, if (is_int) { bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_mul(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_mul(out_val, op1_val, op2_val); } break; case IrBinOpMultWrap: @@ -8481,20 +9053,20 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, break; case IrBinOpDivUnspecified: assert(is_float); - bigfloat_div(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_div(out_val, op1_val, op2_val); break; case IrBinOpDivTrunc: if (is_int) { bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_div_trunc(out_val, op1_val, op2_val); } break; case IrBinOpDivFloor: if (is_int) { bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_div_floor(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_div_floor(out_val, op1_val, op2_val); } break; case IrBinOpDivExact: @@ -8506,10 +9078,10 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, return ErrorExactDivRemainder; } } else { - bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); - BigFloat remainder; - bigfloat_rem(&remainder, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); - if (bigfloat_cmp_zero(&remainder) != CmpEQ) { + float_div_trunc(out_val, op1_val, op2_val); + ConstExprValue remainder; + float_rem(&remainder, op1_val, op2_val); + if (float_cmp_zero(&remainder) != CmpEQ) { return ErrorExactDivRemainder; } } @@ -8518,14 +9090,14 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, if (is_int) { bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_rem(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_rem(out_val, op1_val, op2_val); } break; case IrBinOpRemMod: if (is_int) { bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); } else { - bigfloat_mod(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + float_mod(out_val, op1_val, op2_val); } break; } @@ -8678,16 +9250,16 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; } } else { - if (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) == CmpEQ) { + if (float_cmp_zero(&op2->value) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem ok = true; } else { - BigFloat rem_result; - BigFloat mod_result; - bigfloat_rem(&rem_result, &op1->value.data.x_bigfloat, &op2->value.data.x_bigfloat); - bigfloat_mod(&mod_result, &op1->value.data.x_bigfloat, &op2->value.data.x_bigfloat); - ok = bigfloat_cmp(&rem_result, &mod_result) == CmpEQ; + ConstExprValue rem_result; + ConstExprValue mod_result; + float_rem(&rem_result, &op1->value, &op2->value); + float_mod(&mod_result, &op1->value, &op2->value); + ok = float_cmp(&rem_result, &mod_result) == CmpEQ; } } } @@ -9835,7 +10407,7 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); if (is_float) { - bigfloat_negate(&out_val->data.x_bigfloat, &target_const_val->data.x_bigfloat); + float_negate(out_val, target_const_val); } else if (is_wrap_op) { bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint, expr_type->data.integral.bit_count); @@ -13780,8 +14352,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue codegen->is_big_endian); return; case TypeTableEntryIdFloat: - bigfloat_write_ieee597(&val->data.x_bigfloat, buf, val->type->data.floating.bit_count, - codegen->is_big_endian); + float_write_ieee597(val, buf, codegen->is_big_endian); return; case TypeTableEntryIdPointer: if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { @@ -13841,8 +14412,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue codegen->is_big_endian, val->type->data.integral.is_signed); return; case TypeTableEntryIdFloat: - bigfloat_read_ieee597(&val->data.x_bigfloat, buf, val->type->data.floating.bit_count, - codegen->is_big_endian); + float_read_ieee597(val, buf, codegen->is_big_endian); return; case TypeTableEntryIdPointer: { diff --git a/src/quadmath.hpp b/src/quadmath.hpp new file mode 100644 index 0000000000..7343a11e34 --- /dev/null +++ b/src/quadmath.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_QUADMATH_HPP +#define ZIG_QUADMATH_HPP + +extern "C" { + __float128 fmodq(__float128 a, __float128 b); + __float128 ceilq(__float128 a); + __float128 floorq(__float128 a); + __float128 strtoflt128 (const char *s, char **sp); + int quadmath_snprintf (char *s, size_t size, const char *format, ...); +} + +#endif diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a353e23df6..c8b8c6eb31 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -257,7 +257,7 @@ static void set_token_id(Tokenize *t, Token *token, TokenId id) { if (id == TokenIdIntLiteral) { bigint_init_unsigned(&token->data.int_lit.bigint, 0); } else if (id == TokenIdFloatLiteral) { - bigfloat_init_float(&token->data.float_lit.bigfloat, 0.0); + bigfloat_init_32(&token->data.float_lit.bigfloat, 0.0f); token->data.float_lit.overflow = false; } else if (id == TokenIdStringLiteral || id == TokenIdSymbol) { memset(&token->data.str_lit.str, 0, sizeof(Buf)); @@ -345,7 +345,7 @@ static void end_float_token(Tokenize *t) { uint64_t double_bits = (exponent_bits << 52) | significand_bits; double dbl_value; safe_memcpy(&dbl_value, (double *)&double_bits, 1); - bigfloat_init_float(&t->cur_tok->data.float_lit.bigfloat, dbl_value); + bigfloat_init_64(&t->cur_tok->data.float_lit.bigfloat, dbl_value); } static void end_token(Tokenize *t) { diff --git a/std/math/atan2.zig b/std/math/atan2.zig index 7abb5a2742..13f8a1d384 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -24,7 +24,7 @@ const assert = @import("../debug.zig").assert; pub const atan2 = atan2_workaround; // TODO issue #393 -pub fn atan2_workaround(comptime T: type, x: T, y: T) -> T { +fn atan2_workaround(comptime T: type, x: T, y: T) -> T { switch (T) { f32 => @inlineCall(atan2_32, x, y), f64 => @inlineCall(atan2_64, x, y), diff --git a/std/math/sin.zig b/std/math/sin.zig index c35b6022d0..4fe7565cd3 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -147,6 +147,7 @@ fn sin64(x_: f64) -> f64 { test "math.sin" { assert(sin(f32(0.0)) == sin32(0.0)); assert(sin(f64(0.0)) == sin64(0.0)); + assert(comptime {math.sin(f64(2))} == math.sin(f64(2))); } test "math.sin32" { diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 39aa10018a..96c9f5190a 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -355,3 +355,23 @@ test "@setEvalBranchQuota" { assert(sum == 500500); } } + +test "float literal at compile time not lossy" { + assert(16777216.0 + 1.0 == 16777217.0); + assert(9007199254740992.0 + 1.0 == 9007199254740993.0); +} + +test "f32 at compile time is lossy" { + assert(f32(1 << 24) + 1 == 1 << 24); +} + +test "f64 at compile time is lossy" { + assert(f64(1 << 53) + 1 == 1 << 53); +} + +test "f128 at compile time is lossy" { + assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); +} + +// TODO need a better implementation of bigfloat_init_bigint +// assert(f128(1 << 113) == 10384593717069655257060992658440192);