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}