diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a535d156805f077cd1d46dd89a257c94d75450d6..fb30f3c12cbcb326ae2ea09ea79b2ac61f58ab7b 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -327,61 +327,48 @@ static void end_float_token(Tokenize *t) { } // A SoftFloat-3e float128 is represented internally as a standard - // quad-precision float with 15bit exponent and 113bit fractional. + // quad-precision float with 15bit exponent and 112bit fractional. union { uint64_t repr[2]; float128_t actual; } f_bits; if (bigint_cmp_zero(&t->significand) == CmpEQ) { f_bits.repr[0] = 0; f_bits.repr[1] = 0; } else { - // normalize the significand - if (t->radix == 10) { - zig_panic("TODO: decimal floats"); - } else { - int significand_magnitude_in_bin = 127 - bigint_clz(&t->significand, 128); - t->exponent_in_bin_or_dec += significand_magnitude_in_bin; - if (!(-16382 <= t->exponent_in_bin_or_dec && t->exponent_in_bin_or_dec <= 16383)) { - t->cur_tok->data.float_lit.overflow = true; - return; - } - - const int shift = 112 - significand_magnitude_in_bin; + int significand_magnitude_in_bin = 127 - bigint_clz(&t->significand, 128); + t->exponent_in_bin_or_dec += significand_magnitude_in_bin; + if (!(-16382 <= t->exponent_in_bin_or_dec && t->exponent_in_bin_or_dec <= 16383)) { + t->cur_tok->data.float_lit.overflow = true; + return; + } - // must be special-cased to avoid undefined behavior on shift == 64 - if (shift == 128) { - uint64_t sig_bits[2] = {0, 0}; - bigint_write_twos_complement(&t->significand, (uint8_t*) sig_bits, 128, false); - f_bits.repr[0] = 0; - f_bits.repr[1] = sig_bits[0]; - } else if (shift == 0) { - bigint_write_twos_complement(&t->significand, (uint8_t*) f_bits.repr, 128, false); - } else if (shift >= 64) { - uint64_t sig_bits[2] = {0, 0}; - bigint_write_twos_complement(&t->significand, (uint8_t*) sig_bits, 128, false); - f_bits.repr[0] = 0; - f_bits.repr[1] = sig_bits[0] << (shift - 64); - } else if (shift < 0) { - BigInt shift_bigint; - bigint_init_unsigned(&shift_bigint, -shift); - BigInt shifted_significand; - bigint_shr(&shifted_significand, &t->significand, &shift_bigint); - if (t->exponent_in_bin_or_dec == -1) { - bigint_incr(&shifted_significand); - } - bigint_write_twos_complement(&shifted_significand, (uint8_t*) f_bits.repr, 128, false); - } else { - uint64_t sig_bits[2] = {0, 0}; - bigint_write_twos_complement(&t->significand, (uint8_t*) sig_bits, 128, false); - f_bits.repr[0] = sig_bits[0] << shift; - f_bits.repr[1] = (sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift)); - } + // Shift bits of significand so they are left-justified at the 112-bit + // mark. We truncate excess bits and lose precision. No rounding. + // + // -16 <= shift <= 112 + // + // NOTE: The loss of precision could be considered a limitation of using + // 128-bit floats. In stage2 we should use an arbitrary precision + // float/rational type to represent these and avoid this. + const int shift = 112 - significand_magnitude_in_bin; + bigint_write_twos_complement(&t->significand, (uint8_t*) f_bits.repr, 128, false); - const uint64_t exp_shift = 48; - // Mask the sign bit to 0 since always non-negative lex - const uint64_t exp_mask = 0xffffull << exp_shift; - f_bits.repr[1] &= ~exp_mask; - f_bits.repr[1] |= (uint64_t)(t->exponent_in_bin_or_dec + 16383) << exp_shift; + if (shift >= 64) { + f_bits.repr[1] = f_bits.repr[0] << (shift - 64); + f_bits.repr[0] = 0; + } else if (shift > 0) { + f_bits.repr[1] = (f_bits.repr[1] << shift) | (f_bits.repr[0] >> (64 - shift)); + f_bits.repr[0] = f_bits.repr[0] << shift; + } else if (shift < 0) { + int positive_shift = -shift; + assert(positive_shift <= 16); + f_bits.repr[0] = (f_bits.repr[0] >> positive_shift) | (f_bits.repr[1] << (64 - positive_shift)); + f_bits.repr[1] = f_bits.repr[1] >> positive_shift; } + + // Lexer separates negative sign from value so this is always non-negative. + const uint64_t exp_mask = 0xffffull << 48; + f_bits.repr[1] &= ~exp_mask; + f_bits.repr[1] |= (uint64_t)(t->exponent_in_bin_or_dec + 16383) << 48; } bigfloat_init_128(&t->cur_tok->data.float_lit.bigfloat, f_bits.actual); diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 235ea1dbf6adf62134ce9ba2661d2c983270f651..9b277ce91a7f50561afe5de42a233255d51e44de 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -308,15 +308,27 @@ test "quad hex float literal parsing accurate" { const expected: u128 = 0x3fff1111222233334444555566667777; expect(@bitCast(u128, a) == expected); + // non-normalized + const b: f128 = 0x11.111222233334444555566667777p-4; + expect(@bitCast(u128, b) == expected); + const S = struct { fn doTheTest() void { { - var f1: f128 = 0x1.2eab345678439abcdefea56782346p+5; - expect(@bitCast(u128, f1) == 0x40042eab345678439abcdefea5678234); + var f: f128 = 0x1.2eab345678439abcdefea56782346p+5; + expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234); } { var f: f128 = 0x1.edcb34a235253948765432134674fp-1; - expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134675); + expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); + } + { + var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50; + expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff4f); + } + { + var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9; + expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214567); } const exp2ft = []f64{ 0x1.6a09e667f3bcdp-1,