aboutsummaryrefslogtreecommitdiff
path: root/src/lexical/bhcomp.rs
blob: 6d76c5944e5241a4e6e690003d8be7b3941d2a27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// Adapted from https://github.com/Alexhuszagh/rust-lexical.

//! Compare the mantissa to the halfway representation of the float.
//!
//! Compares the actual significant digits of the mantissa to the
//! theoretical digits from `b+h`, scaled into the proper range.

use super::bignum::*;
use super::digit::*;
use super::exponent::*;
use super::float::*;
use super::math::*;
use super::num::*;
use super::rounding::*;
use crate::lib::{cmp, mem};

// MANTISSA

/// Parse the full mantissa into a big integer.
///
/// Max digits is the maximum number of digits plus one.
fn parse_mantissa<F>(integer: &[u8], fraction: &[u8]) -> Bigint
where
    F: Float,
{
    // Main loop
    let small_powers = POW10_LIMB;
    let step = small_powers.len() - 2;
    let max_digits = F::MAX_DIGITS - 1;
    let mut counter = 0;
    let mut value: Limb = 0;
    let mut i: usize = 0;
    let mut result = Bigint::default();

    // Iteratively process all the data in the mantissa.
    for &digit in integer.iter().chain(fraction) {
        // We've parsed the max digits using small values, add to bignum
        if counter == step {
            result.imul_small(small_powers[counter]);
            result.iadd_small(value);
            counter = 0;
            value = 0;
        }

        value *= 10;
        value += as_limb(to_digit(digit).unwrap());

        i += 1;
        counter += 1;
        if i == max_digits {
            break;
        }
    }

    // We will always have a remainder, as long as we entered the loop
    // once, or counter % step is 0.
    if counter != 0 {
        result.imul_small(small_powers[counter]);
        result.iadd_small(value);
    }

    // If we have any remaining digits after the last value, we need
    // to add a 1 after the rest of the array, it doesn't matter where,
    // just move it up. This is good for the worst-possible float
    // representation. We also need to return an index.
    // Since we already trimmed trailing zeros, we know there has
    // to be a non-zero digit if there are any left.
    if i < integer.len() + fraction.len() {
        result.imul_small(10);
        result.iadd_small(1);
    }

    result
}

// FLOAT OPS

/// Calculate `b` from a a representation of `b` as a float.
#[inline]
pub(super) fn b_extended<F: Float>(f: F) -> ExtendedFloat {
    ExtendedFloat::from_float(f)
}

/// Calculate `b+h` from a a representation of `b` as a float.
#[inline]
pub(super) fn bh_extended<F: Float>(f: F) -> ExtendedFloat {
    // None of these can overflow.
    let b = b_extended(f);
    ExtendedFloat {
        mant: (b.mant << 1) + 1,
        exp: b.exp - 1,
    }
}

// ROUNDING

/// Custom round-nearest, tie-event algorithm for bhcomp.
#[inline]
fn round_nearest_tie_even(fp: &mut ExtendedFloat, shift: i32, is_truncated: bool) {
    let (mut is_above, mut is_halfway) = round_nearest(fp, shift);
    if is_halfway && is_truncated {
        is_above = true;
        is_halfway = false;
    }
    tie_even(fp, is_above, is_halfway);
}

// BHCOMP

/// Calculate the mantissa for a big integer with a positive exponent.
fn large_atof<F>(mantissa: Bigint, exponent: i32) -> F
where
    F: Float,
{
    let bits = mem::size_of::<u64>() * 8;

    // Simple, we just need to multiply by the power of the radix.
    // Now, we can calculate the mantissa and the exponent from this.
    // The binary exponent is the binary exponent for the mantissa
    // shifted to the hidden bit.
    let mut bigmant = mantissa;
    bigmant.imul_pow10(exponent as u32);

    // Get the exact representation of the float from the big integer.
    let (mant, is_truncated) = bigmant.hi64();
    let exp = bigmant.bit_length() as i32 - bits as i32;
    let mut fp = ExtendedFloat { mant, exp };
    fp.round_to_native::<F, _>(|fp, shift| round_nearest_tie_even(fp, shift, is_truncated));
    into_float(fp)
}

/// Calculate the mantissa for a big integer with a negative exponent.
///
/// This invokes the comparison with `b+h`.
fn small_atof<F>(mantissa: Bigint, exponent: i32, f: F) -> F
where
    F: Float,
{
    // Get the significant digits and radix exponent for the real digits.
    let mut real_digits = mantissa;
    let real_exp = exponent;
    debug_assert!(real_exp < 0);

    // Get the significant digits and the binary exponent for `b+h`.
    let theor = bh_extended(f);
    let mut theor_digits = Bigint::from_u64(theor.mant);
    let theor_exp = theor.exp;

    // We need to scale the real digits and `b+h` digits to be the same
    // order. We currently have `real_exp`, in `radix`, that needs to be
    // shifted to `theor_digits` (since it is negative), and `theor_exp`
    // to either `theor_digits` or `real_digits` as a power of 2 (since it
    // may be positive or negative). Try to remove as many powers of 2
    // as possible. All values are relative to `theor_digits`, that is,
    // reflect the power you need to multiply `theor_digits` by.

    // Can remove a power-of-two, since the radix is 10.
    // Both are on opposite-sides of equation, can factor out a
    // power of two.
    //
    // Example: 10^-10, 2^-10   -> ( 0, 10, 0)
    // Example: 10^-10, 2^-15   -> (-5, 10, 0)
    // Example: 10^-10, 2^-5    -> ( 5, 10, 0)
    // Example: 10^-10, 2^5 -> (15, 10, 0)
    let binary_exp = theor_exp - real_exp;
    let halfradix_exp = -real_exp;
    let radix_exp = 0;

    // Carry out our multiplication.
    if halfradix_exp != 0 {
        theor_digits.imul_pow5(halfradix_exp as u32);
    }
    if radix_exp != 0 {
        theor_digits.imul_pow10(radix_exp as u32);
    }
    if binary_exp > 0 {
        theor_digits.imul_pow2(binary_exp as u32);
    } else if binary_exp < 0 {
        real_digits.imul_pow2(-binary_exp as u32);
    }

    // Compare real digits to theoretical digits and round the float.
    match real_digits.compare(&theor_digits) {
        cmp::Ordering::Greater => f.next_positive(),
        cmp::Ordering::Less => f,
        cmp::Ordering::Equal => f.round_positive_even(),
    }
}

/// Calculate the exact value of the float.
///
/// Note: fraction must not have trailing zeros.
pub(crate) fn bhcomp<F>(b: F, integer: &[u8], mut fraction: &[u8], exponent: i32) -> F
where
    F: Float,
{
    // Calculate the number of integer digits and use that to determine
    // where the significant digits start in the fraction.
    let integer_digits = integer.len();
    let fraction_digits = fraction.len();
    let digits_start = if integer_digits == 0 {
        let start = fraction.iter().take_while(|&x| *x == b'0').count();
        fraction = &fraction[start..];
        start
    } else {
        0
    };
    let sci_exp = scientific_exponent(exponent, integer_digits, digits_start);
    let count = F::MAX_DIGITS.min(integer_digits + fraction_digits - digits_start);
    let scaled_exponent = sci_exp + 1 - count as i32;

    let mantissa = parse_mantissa::<F>(integer, fraction);
    if scaled_exponent >= 0 {
        large_atof(mantissa, scaled_exponent)
    } else {
        small_atof(mantissa, scaled_exponent, b)
    }
}