| 1 | //! Conversion of hex-float representation into an accurate value. |
| 2 | // |
| 3 | // Derived from golang strconv/atof.go. |
| 4 | |
| 5 | const std = @import("std"); |
| 6 | const math = std.math; |
| 7 | const common = @import("common.zig"); |
| 8 | const Number = common.Number; |
| 9 | const floatFromUnsigned = common.floatFromUnsigned; |
| 10 | |
| 11 | // converts the form 0xMMM.NNNpEEE. |
| 12 | // |
| 13 | // MMM.NNN = mantissa |
| 14 | // EEE = exponent |
| 15 | // |
| 16 | // MMM.NNN is stored as an integer, the exponent is offset. |
| 17 | pub fn convertHex(comptime T: type, n_: Number(T)) T { |
| 18 | const MantissaT = common.mantissaType(T); |
| 19 | var n = n_; |
| 20 | |
| 21 | if (n.mantissa == 0) { |
| 22 | return if (n.negative) -0.0 else 0.0; |
| 23 | } |
| 24 | |
| 25 | const max_exp = math.floatExponentMax(T); |
| 26 | const min_exp = math.floatExponentMin(T); |
| 27 | const mantissa_bits = math.floatMantissaBits(T); |
| 28 | const fractional_bits = math.floatFractionalBits(T); |
| 29 | const exp_bits = math.floatExponentBits(T); |
| 30 | const exp_bias = min_exp - 1; |
| 31 | |
| 32 | // mantissa now implicitly divided by 2^fractional_bits |
| 33 | n.exponent += fractional_bits; |
| 34 | |
| 35 | // Shift mantissa and exponent to bring representation into float range. |
| 36 | // Eventually we want a mantissa with a leading 1-bit followed by mantbits other bits. |
| 37 | // For rounding, we need two more, where the bottom bit represents |
| 38 | // whether that bit or any later bit was non-zero. |
| 39 | // (If the mantissa has already lost non-zero bits, trunc is true, |
| 40 | // and we OR in a 1 below after shifting left appropriately.) |
| 41 | while (n.mantissa != 0 and n.mantissa >> (mantissa_bits + 2) == 0) { |
| 42 | n.mantissa <<= 1; |
| 43 | n.exponent -= 1; |
| 44 | } |
| 45 | if (n.many_digits) { |
| 46 | n.mantissa |= 1; |
| 47 | } |
| 48 | while (n.mantissa >> (1 + fractional_bits + 2) != 0) { |
| 49 | n.mantissa = (n.mantissa >> 1) | (n.mantissa & 1); |
| 50 | n.exponent += 1; |
| 51 | } |
| 52 | |
| 53 | // If exponent is too negative, |
| 54 | // denormalize in hopes of making it representable. |
| 55 | // (The -2 is for the rounding bits.) |
| 56 | while (n.mantissa > 1 and n.exponent < min_exp - 2) { |
| 57 | n.mantissa = (n.mantissa >> 1) | (n.mantissa & 1); |
| 58 | n.exponent += 1; |
| 59 | } |
| 60 | |
| 61 | // Round using two bottom bits. |
| 62 | var round = n.mantissa & 3; |
| 63 | n.mantissa >>= 2; |
| 64 | round |= n.mantissa & 1; // round to even (round up if mantissa is odd) |
| 65 | n.exponent += 2; |
| 66 | if (round == 3) { |
| 67 | n.mantissa += 1; |
| 68 | if (n.mantissa == 1 << (1 + fractional_bits)) { |
| 69 | n.mantissa >>= 1; |
| 70 | n.exponent += 1; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // Denormal or zero |
| 75 | if (n.mantissa >> fractional_bits == 0) { |
| 76 | n.exponent = exp_bias; |
| 77 | } |
| 78 | |
| 79 | // Infinity and range error |
| 80 | if (n.exponent > max_exp) { |
| 81 | return if (n.negative) -math.inf(T) else math.inf(T); |
| 82 | } |
| 83 | |
| 84 | var bits = n.mantissa & ((1 << mantissa_bits) - 1); |
| 85 | bits |= @as(MantissaT, @intCast((n.exponent - exp_bias) & ((1 << exp_bits) - 1))) << mantissa_bits; |
| 86 | if (n.negative) { |
| 87 | bits |= 1 << (mantissa_bits + exp_bits); |
| 88 | } |
| 89 | return floatFromUnsigned(T, MantissaT, bits); |
| 90 | } |