From 95df4904e8f99b77b6b30f563a5704a2d241a643 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 29 Sep 2026 16:00:28 +0100 Subject: [PATCH] Change `struct Complex128` and `struct Quaternion256` to use `ExactValue` elements to allow for the new `Big_Rat` benefits that floats get --- src/check_builtin.cpp | 29 ++-- src/check_expr.cpp | 69 +++++++-- src/exact_value.cpp | 300 ++++++++++++++++++++----------------- src/llvm_backend_const.cpp | 36 ++--- 4 files changed, 251 insertions(+), 183 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 92ee2299f..60a094408 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3951,10 +3951,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (is_type_complex(t)) { if (x->mode == Addressing_Constant) { + // Keep the conjugate exact: negate the imaginary component(s) as ExactValues. ExactValue v = exact_value_to_complex(x->value); - f64 r = v.value_complex->real; - f64 i = -v.value_complex->imag; - x->value = exact_value_complex(r, i); + ExactValue r = v.value_complex->real; + ExactValue i = exact_unary_operator_value(Token_Sub, v.value_complex->imag, 0, false); + x->value = exact_value_complex_ev(r, i); x->mode = Addressing_Constant; } else { x->mode = Addressing_Value; @@ -3962,11 +3963,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } else if (is_type_quaternion(t)) { if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_quaternion(x->value); - f64 r = +v.value_quaternion->real; - f64 i = -v.value_quaternion->imag; - f64 j = -v.value_quaternion->jmag; - f64 k = -v.value_quaternion->kmag; - x->value = exact_value_quaternion(r, i, j, k); + ExactValue r = v.value_quaternion->real; + ExactValue i = exact_unary_operator_value(Token_Sub, v.value_quaternion->imag, 0, false); + ExactValue j = exact_unary_operator_value(Token_Sub, v.value_quaternion->jmag, 0, false); + ExactValue k = exact_unary_operator_value(Token_Sub, v.value_quaternion->kmag, 0, false); + x->value = exact_value_quaternion_ev(r, i, j, k); x->mode = Addressing_Constant; } else { x->mode = Addressing_Value; @@ -4602,16 +4603,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } case ExactValue_Complex: { - f64 r = operand->value.value_complex->real; - f64 i = operand->value.value_complex->imag; + f64 r = exact_value_to_f64(operand->value.value_complex->real); + f64 i = exact_value_to_f64(operand->value.value_complex->imag); operand->value = exact_value_float(gb_sqrt(r*r + i*i)); break; } case ExactValue_Quaternion: { - f64 r = operand->value.value_quaternion->real; - f64 i = operand->value.value_quaternion->imag; - f64 j = operand->value.value_quaternion->jmag; - f64 k = operand->value.value_quaternion->kmag; + f64 r = exact_value_to_f64(operand->value.value_quaternion->real); + f64 i = exact_value_to_f64(operand->value.value_quaternion->imag); + f64 j = exact_value_to_f64(operand->value.value_quaternion->jmag); + f64 k = exact_value_to_f64(operand->value.value_quaternion->kmag); operand->value = exact_value_float(gb_sqrt(r*r + i*i + j*j + k*k)); break; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9de4d8859..9689d0812 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2338,6 +2338,35 @@ gb_internal bool check_update_float_precision(ExactValue *value, Type *type) { } +gb_internal ExactValue exact_value_round_component_to_float(ExactValue comp, int mantissa_bits, int ebias) { + switch (comp.kind) { + case ExactValue_Rational: + return exact_value_float(big_rat_to_float(&comp.value_rational->num, &comp.value_rational->den, mantissa_bits, ebias)); + case ExactValue_Integer: { + ExactValue r = exact_value_rational_from_integer(&comp.value_integer); + return exact_value_float(big_rat_to_float(&r.value_rational->num, &r.value_rational->den, mantissa_bits, ebias)); + } + } + return exact_value_to_float(comp); +} + +gb_internal void complex_quaternion_element_float_format(Type *type, int *mantissa_bits, int *ebias) { + *mantissa_bits = 52; + *ebias = 1023; + switch (type->Basic.kind) { + case Basic_complex32: + case Basic_quaternion64: + *mantissa_bits = 10; + *ebias = 15; + break; + case Basic_complex64: + case Basic_quaternion128: + *mantissa_bits = 23; + *ebias = 127; + break; + } +} + gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value) { if (in_value.kind == ExactValue_Invalid) { // NOTE(bill): There's already been an error @@ -2597,7 +2626,11 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i ExactValue imag = exact_value_imag(v); if (real.kind != ExactValue_Invalid && imag.kind != ExactValue_Invalid) { - if (out_value) *out_value = exact_value_complex(exact_value_to_f64(real), exact_value_to_f64(imag)); + int mantissa_bits, ebias; + complex_quaternion_element_float_format(type, &mantissa_bits, &ebias); + if (out_value) *out_value = exact_value_complex_ev( + exact_value_round_component_to_float(real, mantissa_bits, ebias), + exact_value_round_component_to_float(imag, mantissa_bits, ebias)); return true; } break; @@ -2625,7 +2658,13 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i ExactValue kmag = exact_value_kmag(v); if (real.kind != ExactValue_Invalid && imag.kind != ExactValue_Invalid) { - if (out_value) *out_value = exact_value_quaternion(exact_value_to_f64(real), exact_value_to_f64(imag), exact_value_to_f64(jmag), exact_value_to_f64(kmag)); + int mantissa_bits, ebias; + complex_quaternion_element_float_format(type, &mantissa_bits, &ebias); + if (out_value) *out_value = exact_value_quaternion_ev( + exact_value_round_component_to_float(real, mantissa_bits, ebias), + exact_value_round_component_to_float(imag, mantissa_bits, ebias), + exact_value_round_component_to_float(jmag, mantissa_bits, ebias), + exact_value_round_component_to_float(kmag, mantissa_bits, ebias)); return true; } break; @@ -5824,42 +5863,42 @@ gb_internal ExactValue get_constant_field(CheckerContext *c, Operand const *oper return value; } else if (value.kind == ExactValue_Quaternion) { // @QuaternionLayout - Quaternion256 q = *value.value_quaternion; + ExactQuaternion q = *value.value_quaternion; GB_ASSERT(sel.index.count == 1); switch (sel.index[0]) { case 3: // w if (success_) *success_ = true; - return exact_value_float(q.real); + return q.real; case 0: // x if (success_) *success_ = true; - return exact_value_float(q.imag); + return q.imag; case 1: // y if (success_) *success_ = true; - return exact_value_float(q.jmag); + return q.jmag; case 2: // z if (success_) *success_ = true; - return exact_value_float(q.kmag); + return q.kmag; } if (success_) *success_ = false; return empty_exact_value; } else if (value.kind == ExactValue_Complex) { // @QuaternionLayout - Complex128 c = *value.value_complex; + ExactComplex c = *value.value_complex; GB_ASSERT(sel.index.count == 1); switch (sel.index[0]) { case 0: // real if (success_) *success_ = true; - return exact_value_float(c.real); + return c.real; case 1: // imag if (success_) *success_ = true; - return exact_value_float(c.imag); + return c.imag; } if (success_) *success_ = false; @@ -13097,15 +13136,15 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) { return big_int_is_zero(&v.value_rational->num); case ExactValue_Complex: if (v.value_complex) { - return v.value_complex->real == 0.0 && v.value_complex->imag == 0.0; + return is_exact_value_zero(v.value_complex->real) && is_exact_value_zero(v.value_complex->imag); } return true; case ExactValue_Quaternion: if (v.value_quaternion) { - return v.value_quaternion->real == 0.0 && - v.value_quaternion->imag == 0.0 && - v.value_quaternion->jmag == 0.0 && - v.value_quaternion->kmag == 0.0; + return is_exact_value_zero(v.value_quaternion->real) && + is_exact_value_zero(v.value_quaternion->imag) && + is_exact_value_zero(v.value_quaternion->jmag) && + is_exact_value_zero(v.value_quaternion->kmag); } return true; case ExactValue_Pointer: diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 1cd282c2a..ff7ec97e9 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -7,12 +7,10 @@ struct Type; struct Entity; gb_internal bool are_types_identical(Type *x, Type *y); -struct Complex128 { - f64 real, imag; -}; -struct Quaternion256 { - f64 imag, jmag, kmag, real; -}; +// NOTE(bill): Defined after ExactValue below, since their components are now exact values +// (each a Float=f64 or Rational=big_rat) so complex/quaternion constant folding stays exact. +struct ExactComplex; +struct ExactQuaternion; enum ExactValueKind { ExactValue_Invalid = 0, @@ -57,24 +55,33 @@ gb_global char const *exact_value_kind_string[ExactValue_Count] = { struct ExactValue { ExactValueKind kind; union { - bool value_bool; - String value_string; - BigInt value_integer; - f64 value_float; - BigRat * value_rational; - i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer - Complex128 *value_complex; - Quaternion256 *value_quaternion; - Ast * value_compound; - Ast * value_procedure; - Type * value_typeid; - String16 value_string16; - Ast * value_asm_template; - Ast * value_variant; + bool value_bool; + String value_string; + BigInt value_integer; + f64 value_float; + BigRat * value_rational; + i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer + ExactComplex *value_complex; + ExactQuaternion *value_quaternion; + Ast * value_compound; + Ast * value_procedure; + Type * value_typeid; + String16 value_string16; + Ast * value_asm_template; + Ast * value_variant; }; Type *variant_type; }; +// Complex/quaternion components are exact numeric values (Integer/Rational/Float), so their constant +// arithmetic keeps full precision until the value is rounded to a concrete type. +struct ExactComplex { + ExactValue real, imag; +}; +struct ExactQuaternion { + ExactValue imag, jmag, kmag, real; +}; + gb_global ExactValue const empty_exact_value = {}; gb_internal uintptr hash_exact_value(ExactValue v) { @@ -115,10 +122,14 @@ gb_internal uintptr hash_exact_value(ExactValue v) { res = ptr_map_hash_key(v.value_pointer); break; case ExactValue_Complex: - res = gb_fnv32a(v.value_complex, gb_size_of(Complex128)); + res = hash_exact_value(v.value_complex->real) ^ + (hash_exact_value(v.value_complex->imag) * 0x01000193); break; case ExactValue_Quaternion: - res = gb_fnv32a(v.value_quaternion, gb_size_of(Quaternion256)); + res = hash_exact_value(v.value_quaternion->real) ^ + (hash_exact_value(v.value_quaternion->imag) * 0x01000193) ^ + (hash_exact_value(v.value_quaternion->jmag) * 0x01000193) ^ + (hash_exact_value(v.value_quaternion->kmag) * 0x01000193); break; case ExactValue_Compound: res = ptr_map_hash_key(v.value_compound); @@ -204,17 +215,26 @@ gb_internal ExactValue exact_value_rational_from_integer(BigInt const *i) { return exact_value_rational_from_ints(i, &one); } -gb_internal ExactValue exact_value_complex(f64 real, f64 imag) { +// Promote an integer to an exact rational, leaving other kinds untouched. Used so complex/quaternion +// division does true rational division rather than the integer `Token_Quo` path (which is `fmod`). +gb_internal ExactValue exact_value_as_rational_if_integer(ExactValue v) { + if (v.kind == ExactValue_Integer) { + return exact_value_rational_from_integer(&v.value_integer); + } + return v; +} + +// Exact-component constructors: each component is a numeric ExactValue (Integer/Rational/Float). +gb_internal ExactValue exact_value_complex_ev(ExactValue real, ExactValue imag) { ExactValue result = {ExactValue_Complex}; - result.value_complex = permanent_alloc_item(); + result.value_complex = permanent_alloc_item(); result.value_complex->real = real; result.value_complex->imag = imag; return result; } - -gb_internal ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { +gb_internal ExactValue exact_value_quaternion_ev(ExactValue real, ExactValue imag, ExactValue jmag, ExactValue kmag) { ExactValue result = {ExactValue_Quaternion}; - result.value_quaternion = permanent_alloc_item(); + result.value_quaternion = permanent_alloc_item(); result.value_quaternion->real = real; result.value_quaternion->imag = imag; result.value_quaternion->jmag = jmag; @@ -222,6 +242,14 @@ gb_internal ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 return result; } +gb_internal ExactValue exact_value_complex(f64 real, f64 imag) { + return exact_value_complex_ev(exact_value_float(real), exact_value_float(imag)); +} + +gb_internal ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { + return exact_value_quaternion_ev(exact_value_float(real), exact_value_float(imag), exact_value_float(jmag), exact_value_float(kmag)); +} + gb_internal ExactValue exact_value_pointer(i64 ptr) { ExactValue result = {ExactValue_Pointer}; result.value_pointer = ptr; @@ -448,12 +476,15 @@ gb_internal ExactValue exact_value_from_basic_literal(TokenKind kind, String con String str = string; Rune last_rune = cast(Rune)str[str.len-1]; str.len--; // Ignore the 'i|j|k' - f64 imag = float_from_string(str); + // Parse the magnitude with the same exact (rational) path as an ordinary float literal so the + // imaginary component keeps full precision rather than being pre-rounded to f64. + ExactValue imag = exact_value_float_from_string(str); + ExactValue zero = exact_value_i64(0); switch (last_rune) { - case 'i': return exact_value_complex(0, imag); - case 'j': return exact_value_quaternion(0, 0, imag, 0); - case 'k': return exact_value_quaternion(0, 0, 0, imag); + case 'i': return exact_value_complex_ev(zero, imag); + case 'j': return exact_value_quaternion_ev(zero, zero, imag, zero); + case 'k': return exact_value_quaternion_ev(zero, zero, zero, imag); default: GB_PANIC("Invalid imaginary basic literal"); } } @@ -532,35 +563,27 @@ gb_internal ExactValue exact_value_to_float(ExactValue v) { gb_internal ExactValue exact_value_to_complex(ExactValue v) { switch (v.kind) { case ExactValue_Integer: - return exact_value_complex(big_int_to_f64(&v.value_integer), 0); case ExactValue_Float: - return exact_value_complex(v.value_float, 0); case ExactValue_Rational: - return exact_value_complex(big_rat_to_f64(&v.value_rational->num, &v.value_rational->den), 0); + return exact_value_complex_ev(v, exact_value_i64(0)); // keep the real component exact case ExactValue_Complex: return v; - // case ExactValue_Quaternion: - // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); } ExactValue r = {ExactValue_Invalid}; - v.value_complex = permanent_alloc_item(); return r; } gb_internal ExactValue exact_value_to_quaternion(ExactValue v) { switch (v.kind) { case ExactValue_Integer: - return exact_value_quaternion(big_int_to_f64(&v.value_integer), 0, 0, 0); case ExactValue_Float: - return exact_value_quaternion(v.value_float, 0, 0, 0); case ExactValue_Rational: - return exact_value_quaternion(big_rat_to_f64(&v.value_rational->num, &v.value_rational->den), 0, 0, 0); + return exact_value_quaternion_ev(v, exact_value_i64(0), exact_value_i64(0), exact_value_i64(0)); case ExactValue_Complex: - return exact_value_quaternion(v.value_complex->real, v.value_complex->imag, 0, 0); + return exact_value_quaternion_ev(v.value_complex->real, v.value_complex->imag, exact_value_i64(0), exact_value_i64(0)); case ExactValue_Quaternion: return v; } ExactValue r = {ExactValue_Invalid}; - v.value_quaternion = permanent_alloc_item(); return r; } @@ -571,9 +594,9 @@ gb_internal ExactValue exact_value_real(ExactValue v) { case ExactValue_Rational: return v; case ExactValue_Complex: - return exact_value_float(v.value_complex->real); + return v.value_complex->real; case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion->real); + return v.value_quaternion->real; } ExactValue r = {ExactValue_Invalid}; return r; @@ -586,9 +609,9 @@ gb_internal ExactValue exact_value_imag(ExactValue v) { case ExactValue_Rational: return exact_value_i64(0); case ExactValue_Complex: - return exact_value_float(v.value_complex->imag); + return v.value_complex->imag; case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion->imag); + return v.value_quaternion->imag; } ExactValue r = {ExactValue_Invalid}; return r; @@ -602,7 +625,7 @@ gb_internal ExactValue exact_value_jmag(ExactValue v) { case ExactValue_Complex: return exact_value_i64(0); case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion->jmag); + return v.value_quaternion->jmag; } ExactValue r = {ExactValue_Invalid}; return r; @@ -616,7 +639,7 @@ gb_internal ExactValue exact_value_kmag(ExactValue v) { case ExactValue_Complex: return exact_value_i64(0); case ExactValue_Quaternion: - return exact_value_float(v.value_quaternion->kmag); + return v.value_quaternion->kmag; } ExactValue r = {ExactValue_Invalid}; return r; @@ -724,16 +747,16 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3 return exact_value_rational_from_ints(&n, &v.value_rational->den); } case ExactValue_Complex: { - f64 real = v.value_complex->real; - f64 imag = v.value_complex->imag; - return exact_value_complex(-real, -imag); + ExactValue re = exact_unary_operator_value(Token_Sub, v.value_complex->real, precision, is_unsigned); + ExactValue im = exact_unary_operator_value(Token_Sub, v.value_complex->imag, precision, is_unsigned); + return exact_value_complex_ev(re, im); } case ExactValue_Quaternion: { - f64 real = v.value_quaternion->real; - f64 imag = v.value_quaternion->imag; - f64 jmag = v.value_quaternion->jmag; - f64 kmag = v.value_quaternion->kmag; - return exact_value_quaternion(-real, -imag, -jmag, -kmag); + ExactValue re = exact_unary_operator_value(Token_Sub, v.value_quaternion->real, precision, is_unsigned); + ExactValue im = exact_unary_operator_value(Token_Sub, v.value_quaternion->imag, precision, is_unsigned); + ExactValue jm = exact_unary_operator_value(Token_Sub, v.value_quaternion->jmag, precision, is_unsigned); + ExactValue km = exact_unary_operator_value(Token_Sub, v.value_quaternion->kmag, precision, is_unsigned); + return exact_value_quaternion_ev(re, im, jm, km); } } break; @@ -838,10 +861,10 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) { *x = exact_value_float(big_int_to_f64(&x->value_integer)); return; case ExactValue_Complex: - *x = exact_value_complex(big_int_to_f64(&x->value_integer), 0); + *x = exact_value_to_complex(*x); // keep the integer component exact return; case ExactValue_Quaternion: - *x = exact_value_quaternion(big_int_to_f64(&x->value_integer), 0, 0, 0); + *x = exact_value_to_quaternion(*x); // keep the integer component exact return; } break; @@ -972,86 +995,103 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E } case ExactValue_Complex: { + // Exact per-component arithmetic (each component is an Integer/Rational/Float ExactValue). + #define EV_MUL(p, q) exact_binary_operator_value(Token_Mul, (p), (q)) + #define EV_ADD(p, q) exact_binary_operator_value(Token_Add, (p), (q)) + #define EV_SUB(p, q) exact_binary_operator_value(Token_Sub, (p), (q)) + #define EV_QUO(p, q) exact_binary_operator_value(Token_Quo, exact_value_as_rational_if_integer(p), exact_value_as_rational_if_integer(q)) y = exact_value_to_complex(y); - f64 a = x.value_complex->real; - f64 b = x.value_complex->imag; - f64 c = y.value_complex->real; - f64 d = y.value_complex->imag; - f64 real = 0; - f64 imag = 0; + ExactValue a = x.value_complex->real; + ExactValue b = x.value_complex->imag; + ExactValue c = y.value_complex->real; + ExactValue d = y.value_complex->imag; + ExactValue real = {}; + ExactValue imag = {}; switch (op) { case Token_Add: - real = a + c; - imag = b + d; + real = EV_ADD(a, c); + imag = EV_ADD(b, d); break; case Token_Sub: - real = a - c; - imag = b - d; + real = EV_SUB(a, c); + imag = EV_SUB(b, d); break; case Token_Mul: - real = (a*c - b*d); - imag = (b*c + a*d); + real = EV_SUB(EV_MUL(a, c), EV_MUL(b, d)); // a*c - b*d + imag = EV_ADD(EV_MUL(b, c), EV_MUL(a, d)); // b*c + a*d break; case Token_Quo: { - f64 s = c*c + d*d; - real = (a*c + b*d)/s; - imag = (b*c - a*d)/s; + ExactValue s = EV_ADD(EV_MUL(c, c), EV_MUL(d, d)); // c*c + d*d + real = EV_QUO(EV_ADD(EV_MUL(a, c), EV_MUL(b, d)), s); // (a*c + b*d)/s + imag = EV_QUO(EV_SUB(EV_MUL(b, c), EV_MUL(a, d)), s); // (b*c - a*d)/s break; } default: goto error; } - return exact_value_complex(real, imag); - break; + return exact_value_complex_ev(real, imag); + #undef EV_MUL + #undef EV_ADD + #undef EV_SUB + #undef EV_QUO } case ExactValue_Quaternion: { + #define EV_MUL(p, q) exact_binary_operator_value(Token_Mul, (p), (q)) + #define EV_ADD(p, q) exact_binary_operator_value(Token_Add, (p), (q)) + #define EV_SUB(p, q) exact_binary_operator_value(Token_Sub, (p), (q)) + #define EV_QUO(p, q) exact_binary_operator_value(Token_Quo, exact_value_as_rational_if_integer(p), exact_value_as_rational_if_integer(q)) + #define EV_NEG(p) exact_unary_operator_value(Token_Sub, (p), 0, false) y = exact_value_to_quaternion(y); - f64 xr = x.value_quaternion->real; - f64 xi = x.value_quaternion->imag; - f64 xj = x.value_quaternion->jmag; - f64 xk = x.value_quaternion->kmag; - f64 yr = y.value_quaternion->real; - f64 yi = y.value_quaternion->imag; - f64 yj = y.value_quaternion->jmag; - f64 yk = y.value_quaternion->kmag; + ExactValue xr = x.value_quaternion->real; + ExactValue xi = x.value_quaternion->imag; + ExactValue xj = x.value_quaternion->jmag; + ExactValue xk = x.value_quaternion->kmag; + ExactValue yr = y.value_quaternion->real; + ExactValue yi = y.value_quaternion->imag; + ExactValue yj = y.value_quaternion->jmag; + ExactValue yk = y.value_quaternion->kmag; - - f64 real = 0; - f64 imag = 0; - f64 jmag = 0; - f64 kmag = 0; + ExactValue real = {}; + ExactValue imag = {}; + ExactValue jmag = {}; + ExactValue kmag = {}; switch (op) { case Token_Add: - real = xr + yr; - imag = xi + yi; - jmag = xj + yj; - kmag = xk + yk; + real = EV_ADD(xr, yr); imag = EV_ADD(xi, yi); jmag = EV_ADD(xj, yj); kmag = EV_ADD(xk, yk); break; case Token_Sub: - real = xr - yr; - imag = xi - yi; - jmag = xj - yj; - kmag = xk - yk; + real = EV_SUB(xr, yr); imag = EV_SUB(xi, yi); jmag = EV_SUB(xj, yj); kmag = EV_SUB(xk, yk); break; case Token_Mul: - imag = xr * yi + xi * yr + xj * yk - xk * yj; - jmag = xr * yj - xi * yk + xj * yr + xk * yi; - kmag = xr * yk + xi * yj - xj * yi + xk * yr; - real = xr * yr - xi * yi - xj * yj - xk * yk; + // Hamilton product (matches the previous f64 formulas term-for-term). + imag = EV_SUB(EV_ADD(EV_ADD(EV_MUL(xr, yi), EV_MUL(xi, yr)), EV_MUL(xj, yk)), EV_MUL(xk, yj)); + jmag = EV_ADD(EV_ADD(EV_SUB(EV_MUL(xr, yj), EV_MUL(xi, yk)), EV_MUL(xj, yr)), EV_MUL(xk, yi)); + kmag = EV_ADD(EV_SUB(EV_ADD(EV_MUL(xr, yk), EV_MUL(xi, yj)), EV_MUL(xj, yi)), EV_MUL(xk, yr)); + real = EV_SUB(EV_SUB(EV_SUB(EV_MUL(xr, yr), EV_MUL(xi, yi)), EV_MUL(xj, yj)), EV_MUL(xk, yk)); break; case Token_Quo: { - f64 invmag2 = 1.0 / (yr*yr + yi*yi + yj*yj + yk*yk); - imag = (xr * -yi + xi * +yr + xj * -yk - xk * -yj) * invmag2; - jmag = (xr * -yj - xi * -yk + xj * +yr + xk * -yi) * invmag2; - kmag = (xr * -yk + xi * -yj - xj * -yi + xk * +yr) * invmag2; - real = (xr * +yr - xi * -yi - xj * -yj - xk * -yk) * invmag2; + // q1 / q2 = q1 * conj(q2) / |q2|^2 + ExactValue nyi = EV_NEG(yi), nyj = EV_NEG(yj), nyk = EV_NEG(yk); + ExactValue mag2 = EV_ADD(EV_ADD(EV_ADD(EV_MUL(yr, yr), EV_MUL(yi, yi)), EV_MUL(yj, yj)), EV_MUL(yk, yk)); + imag = EV_SUB(EV_ADD(EV_ADD(EV_MUL(xr, nyi), EV_MUL(xi, yr)), EV_MUL(xj, nyk)), EV_MUL(xk, nyj)); + jmag = EV_ADD(EV_ADD(EV_SUB(EV_MUL(xr, nyj), EV_MUL(xi, nyk)), EV_MUL(xj, yr)), EV_MUL(xk, nyi)); + kmag = EV_ADD(EV_SUB(EV_ADD(EV_MUL(xr, nyk), EV_MUL(xi, nyj)), EV_MUL(xj, nyi)), EV_MUL(xk, yr)); + real = EV_SUB(EV_SUB(EV_SUB(EV_MUL(xr, yr), EV_MUL(xi, nyi)), EV_MUL(xj, nyj)), EV_MUL(xk, nyk)); + imag = EV_QUO(imag, mag2); + jmag = EV_QUO(jmag, mag2); + kmag = EV_QUO(kmag, mag2); + real = EV_QUO(real, mag2); break; } default: goto error; } - return exact_value_quaternion(real, imag, jmag, kmag); - break; + return exact_value_quaternion_ev(real, imag, jmag, kmag); + #undef EV_MUL + #undef EV_ADD + #undef EV_SUB + #undef EV_QUO + #undef EV_NEG } case ExactValue_String: { @@ -1175,40 +1215,28 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) } case ExactValue_Complex: { - f64 a = x.value_complex->real; - f64 b = x.value_complex->imag; - f64 c = y.value_complex->real; - f64 d = y.value_complex->imag; - if (isnan(a) || isnan(b) || isnan(c) || isnan(d)) { - return op == Token_NotEq; - } - + // Compare component-wise using exact comparisons (each component is a numeric ExactValue). + ExactComplex a = *x.value_complex; + ExactComplex b = *y.value_complex; + bool real_eq = compare_exact_values(Token_CmpEq, a.real, b.real); + bool imag_eq = compare_exact_values(Token_CmpEq, a.imag, b.imag); switch (op) { - case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0; - case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0; + case Token_CmpEq: return real_eq && imag_eq; + case Token_NotEq: return !real_eq || !imag_eq; } break; } case ExactValue_Quaternion: { - Quaternion256 a = *x.value_quaternion; - Quaternion256 b = *y.value_quaternion; - if (isnan(a.real) || isnan(a.imag) || isnan(a.jmag) || isnan(a.kmag) || - isnan(b.real) || isnan(b.imag) || isnan(b.jmag) || isnan(b.kmag)) { - return op == Token_NotEq; - } - + ExactQuaternion a = *x.value_quaternion; + ExactQuaternion b = *y.value_quaternion; + bool real_eq = compare_exact_values(Token_CmpEq, a.real, b.real); + bool imag_eq = compare_exact_values(Token_CmpEq, a.imag, b.imag); + bool jmag_eq = compare_exact_values(Token_CmpEq, a.jmag, b.jmag); + bool kmag_eq = compare_exact_values(Token_CmpEq, a.kmag, b.kmag); switch (op) { - case Token_CmpEq: - return cmp_f64(a.real, b.real) == 0 && - cmp_f64(a.imag, b.imag) == 0 && - cmp_f64(a.jmag, b.jmag) == 0 && - cmp_f64(a.kmag, b.kmag) == 0; - case Token_NotEq: - return cmp_f64(a.real, b.real) != 0 || - cmp_f64(a.imag, b.imag) != 0 || - cmp_f64(a.jmag, b.jmag) != 0 || - cmp_f64(a.kmag, b.kmag) != 0; + case Token_CmpEq: return real_eq && imag_eq && jmag_eq && kmag_eq; + case Token_NotEq: return !real_eq || !imag_eq || !jmag_eq || !kmag_eq; } break; } @@ -1349,9 +1377,9 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const } return gb_string_append_fmt(str, "%.17g", big_rat_to_f64(&v.value_rational->num, &v.value_rational->den)); case ExactValue_Complex: - return gb_string_append_fmt(str, "%.17g+%.17gi", v.value_complex->real, v.value_complex->imag); + return gb_string_append_fmt(str, "%.17g+%.17gi", exact_value_to_f64(v.value_complex->real), exact_value_to_f64(v.value_complex->imag)); case ExactValue_Quaternion: - return gb_string_append_fmt(str, "%.17g+%.17gi+%.17gj+%.17gk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag); + return gb_string_append_fmt(str, "%.17g+%.17gi+%.17gj+%.17gk", exact_value_to_f64(v.value_quaternion->real), exact_value_to_f64(v.value_quaternion->imag), exact_value_to_f64(v.value_quaternion->jmag), exact_value_to_f64(v.value_quaternion->kmag)); case ExactValue_Pointer: return str; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index f86678727..49d59d62f 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1353,16 +1353,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb LLVMValueRef values[2] = {}; switch (8*type_size_of(type)) { case 32: - values[0] = lb_const_f16(m, cast(f32)value.value_complex->real); - values[1] = lb_const_f16(m, cast(f32)value.value_complex->imag); + values[0] = lb_const_f16(m, cast(f32)exact_value_to_f64(value.value_complex->real)); + values[1] = lb_const_f16(m, cast(f32)exact_value_to_f64(value.value_complex->imag)); break; case 64: - values[0] = lb_const_f32(m, cast(f32)value.value_complex->real); - values[1] = lb_const_f32(m, cast(f32)value.value_complex->imag); + values[0] = lb_const_f32(m, cast(f32)exact_value_to_f64(value.value_complex->real)); + values[1] = lb_const_f32(m, cast(f32)exact_value_to_f64(value.value_complex->imag)); break; case 128: - values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->real); - values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->imag); + values[0] = LLVMConstReal(lb_type(m, t_f64), exact_value_to_f64(value.value_complex->real)); + values[1] = LLVMConstReal(lb_type(m, t_f64), exact_value_to_f64(value.value_complex->imag)); break; } @@ -1376,24 +1376,24 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb switch (8*type_size_of(type)) { case 64: // @QuaternionLayout - values[3] = lb_const_f16(m, cast(f32)value.value_quaternion->real); - values[0] = lb_const_f16(m, cast(f32)value.value_quaternion->imag); - values[1] = lb_const_f16(m, cast(f32)value.value_quaternion->jmag); - values[2] = lb_const_f16(m, cast(f32)value.value_quaternion->kmag); + values[3] = lb_const_f16(m, cast(f32)exact_value_to_f64(value.value_quaternion->real)); + values[0] = lb_const_f16(m, cast(f32)exact_value_to_f64(value.value_quaternion->imag)); + values[1] = lb_const_f16(m, cast(f32)exact_value_to_f64(value.value_quaternion->jmag)); + values[2] = lb_const_f16(m, cast(f32)exact_value_to_f64(value.value_quaternion->kmag)); break; case 128: // @QuaternionLayout - values[3] = lb_const_f32(m, cast(f32)value.value_quaternion->real); - values[0] = lb_const_f32(m, cast(f32)value.value_quaternion->imag); - values[1] = lb_const_f32(m, cast(f32)value.value_quaternion->jmag); - values[2] = lb_const_f32(m, cast(f32)value.value_quaternion->kmag); + values[3] = lb_const_f32(m, cast(f32)exact_value_to_f64(value.value_quaternion->real)); + values[0] = lb_const_f32(m, cast(f32)exact_value_to_f64(value.value_quaternion->imag)); + values[1] = lb_const_f32(m, cast(f32)exact_value_to_f64(value.value_quaternion->jmag)); + values[2] = lb_const_f32(m, cast(f32)exact_value_to_f64(value.value_quaternion->kmag)); break; case 256: // @QuaternionLayout - values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->real); - values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->imag); - values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->jmag); - values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->kmag); + values[3] = LLVMConstReal(lb_type(m, t_f64), exact_value_to_f64(value.value_quaternion->real)); + values[0] = LLVMConstReal(lb_type(m, t_f64), exact_value_to_f64(value.value_quaternion->imag)); + values[1] = LLVMConstReal(lb_type(m, t_f64), exact_value_to_f64(value.value_quaternion->jmag)); + values[2] = LLVMConstReal(lb_type(m, t_f64), exact_value_to_f64(value.value_quaternion->kmag)); break; }