Commit e3b70fe4ba

Marc Tiehuis <marctiehuis@gmail.com>
2019-03-22 05:11:57
Simplify hex-float parsing code
1 parent d04a145
Changed files (2)
src
test
stage1
behavior
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;
-
-            // 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));
-            }
+        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 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;
+        // 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);
+
+        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);
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,