master
 1//! Conversion of hex-float representation into an accurate value.
 2//
 3// Derived from golang strconv/atof.go.
 4
 5const std = @import("std");
 6const math = std.math;
 7const common = @import("common.zig");
 8const Number = common.Number;
 9const 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.
17pub 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}