Commit 9dfe217be3

Marc Tiehuis <marctiehuis@gmail.com>
2017-09-28 08:15:06
Allow 128-bit hex float literals
Closes #499.
1 parent fd5a5db
Changed files (5)
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;
+            }
+
+            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 {
-                // 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;
+                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) {
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;
         }
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;
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" {
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");