diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a7820678d..d2d45ed46 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -303,7 +303,7 @@ static void end_float_token(Tokenize *t) { return; } - if (!bigint_fits_in_bits(&t->specified_exponent, 64, true)) { + if (!bigint_fits_in_bits(&t->specified_exponent, 128, true)) { t->cur_tok->data.float_lit.overflow = true; return; } @@ -314,39 +314,52 @@ static void end_float_token(Tokenize *t) { } t->exponent_in_bin_or_dec = (int)(t->exponent_in_bin_or_dec + specified_exponent); - if (!bigint_fits_in_bits(&t->significand, 64, false)) { + if (!bigint_fits_in_bits(&t->significand, 128, false)) { t->cur_tok->data.float_lit.overflow = true; return; } - uint64_t significand = bigint_as_unsigned(&t->significand); - uint64_t significand_bits; - uint64_t exponent_bits; - if (significand == 0) { - // 0 is all 0's - significand_bits = 0; - exponent_bits = 0; + // A SoftFloat-3d float128 is represented internally as a standard + // quad-precision float with 15bit exponent and 113bit 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 = clzll(1) - clzll(significand); + int significand_magnitude_in_bin = 127 - bigint_clz(&t->significand, 128); t->exponent_in_bin_or_dec += significand_magnitude_in_bin; - if (!(-1022 <= t->exponent_in_bin_or_dec && t->exponent_in_bin_or_dec <= 1023)) { + if (!(-16382 <= t->exponent_in_bin_or_dec && t->exponent_in_bin_or_dec <= 16383)) { t->cur_tok->data.float_lit.overflow = true; return; - } else { - // this should chop off exactly one 1 bit from the top. - significand_bits = ((uint64_t)significand << (52 - significand_magnitude_in_bin)) & 0xfffffffffffffULL; - exponent_bits = t->exponent_in_bin_or_dec + 1023; } + + uint64_t sig_bits[2] = {0, 0}; + bigint_write_twos_complement(&t->significand, (uint8_t*) sig_bits, 128, false); + + const uint64_t shift = 112 - significand_magnitude_in_bin; + const uint64_t exp_shift = 48; + // Mask the sign bit to 0 since always non-negative lex + const uint64_t exp_mask = 0xfffful << exp_shift; + + if (shift >= 64) { + f_bits.repr[0] = 0; + f_bits.repr[1] = sig_bits[0] << (shift - 64); + } else { + f_bits.repr[0] = sig_bits[0] << shift; + f_bits.repr[1] = ((sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift))); + } + + f_bits.repr[1] &= ~exp_mask; + f_bits.repr[1] |= (uint64_t)(t->exponent_in_bin_or_dec + 16383) << exp_shift; } } - uint64_t double_bits = (exponent_bits << 52) | significand_bits; - double dbl_value; - safe_memcpy(&dbl_value, (double *)&double_bits, 1); - bigfloat_init_64(&t->cur_tok->data.float_lit.bigfloat, dbl_value); + + bigfloat_init_128(&t->cur_tok->data.float_lit.bigfloat, f_bits.actual); } static void end_token(Tokenize *t) { diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 5ee28e786..ef0b2766b 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -255,7 +255,7 @@ fn expm1_64(x_: f64) -> f64 { if (k < 0 or k > 56) { var y = x - e + 1.0; if (k == 1024) { - y = y * 2.0 * 0x1.0p1022 * 10; + y = y * 2.0 * 0x1.0p1023; } else { y = y * twopk; } diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig index bb1e208b9..6e8219449 100644 --- a/std/math/scalbn.zig +++ b/std/math/scalbn.zig @@ -45,10 +45,10 @@ fn scalbn64(x: f64, n_: i32) -> f64 { var n = n_; if (n > 1023) { - y *= 0x1.0p1022 * 2.0; + y *= 0x1.0p1023; n -= 1023; if (n > 1023) { - y *= 0x1.0p1022 * 2.0; + y *= 0x1.0p1023; n -= 1023; if (n > 1023) { n = 1023; diff --git a/test/cases/math.zig b/test/cases/math.zig index f950db1a1..1d29800aa 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -241,14 +241,21 @@ test "allow signed integer division/remainder when values are comptime known and assert(-6 % 3 == 0); } -test "float literal parsing" { +test "hex float literal parsing" { comptime assert(0x1.0 == 1.0); } +test "quad hex float literal parsing in range" { + const a = 0x1.af23456789bbaaab347645365cdep+5; + const b = 0x1.dedafcff354b6ae9758763545432p-9; + const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; + const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; +} + test "hex float literal within range" { - const a = 0x1.0p1023; - const b = 0x0.1p1027; - const c = 0x1.0p-1022; + const a = 0x1.0p16383; + const b = 0x0.1p16387; + const c = 0x1.0p-16382; } test "truncating shift left" { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a8a4a96e8..fc74cc1bf 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1901,14 +1901,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) { cases.add("float literal too large error", \\comptime { - \\ const a = 0x1.0p1024; + \\ const a = 0x1.0p16384; \\} , ".tmp_source.zig:2:15: error: float literal out of range of any type"); cases.add("float literal too small error (denormal)", \\comptime { - \\ const a = 0x1.0p-1023; + \\ const a = 0x1.0p-16384; \\} , ".tmp_source.zig:2:15: error: float literal out of range of any type");