Coverage Report

Created: 2026-01-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/lexical-parse-float-1.0.6/src/slow.rs
Line
Count
Source
1
//! Slow, fallback cases where we cannot unambiguously round a float.
2
//!
3
//! This occurs when we cannot determine the exact representation using
4
//! both the fast path (native) cases nor the Lemire/Bellerophon algorithms,
5
//! and therefore must fallback to a slow, arbitrary-precision representation.
6
7
#![doc(hidden)]
8
9
use core::cmp;
10
11
#[cfg(not(feature = "compact"))]
12
use lexical_parse_integer::algorithm;
13
use lexical_util::digit::char_to_valid_digit_const;
14
#[cfg(feature = "radix")]
15
use lexical_util::digit::digit_to_char_const;
16
use lexical_util::format::NumberFormat;
17
use lexical_util::iterator::{AsBytes, DigitsIter, Iter};
18
use lexical_util::num::{AsPrimitive, Integer};
19
20
#[cfg(feature = "radix")]
21
use crate::bigint::Bigfloat;
22
use crate::bigint::{Bigint, Limb};
23
use crate::float::{extended_to_float, ExtendedFloat80, RawFloat};
24
use crate::limits::{u32_power_limit, u64_power_limit};
25
use crate::number::Number;
26
use crate::shared;
27
28
// ALGORITHM
29
// ---------
30
31
/// Parse the significant digits and biased, binary exponent of a float.
32
///
33
/// This is a fallback algorithm that uses a big-integer representation
34
/// of the float, and therefore is considerably slower than faster
35
/// approximations. However, it will always determine how to round
36
/// the significant digits to the nearest machine float, allowing
37
/// use to handle near half-way cases.
38
///
39
/// Near half-way cases are halfway between two consecutive machine floats.
40
/// For example, the float `16777217.0` has a bitwise representation of
41
/// `100000000000000000000000 1`. Rounding to a single-precision float,
42
/// the trailing `1` is truncated. Using round-nearest, tie-even, any
43
/// value above `16777217.0` must be rounded up to `16777218.0`, while
44
/// any value before or equal to `16777217.0` must be rounded down
45
/// to `16777216.0`. These near-halfway conversions therefore may require
46
/// a large number of digits to unambiguously determine how to round.
47
#[must_use]
48
#[allow(clippy::unwrap_used)] // reason = "none is a developer error"
49
14
pub fn slow_radix<F: RawFloat, const FORMAT: u128>(
50
14
    num: Number,
51
14
    fp: ExtendedFloat80,
52
14
) -> ExtendedFloat80 {
53
    // Ensure our preconditions are valid:
54
    //  1. The significant digits are not shifted into place.
55
14
    debug_assert!(fp.mant & (1 << 63) != 0, "number must be normalized");
56
57
14
    let format = NumberFormat::<{ FORMAT }> {};
58
59
    // This assumes the sign bit has already been parsed, and we're
60
    // starting with the integer digits, and the float format has been
61
    // correctly validated.
62
14
    let sci_exp = scientific_exponent::<FORMAT>(&num);
63
64
    // We have 3 major algorithms we use for this:
65
    //  1. An algorithm with a finite number of digits and a positive exponent.
66
    //  2. An algorithm with a finite number of digits and a negative exponent.
67
    //  3. A fallback algorithm with a non-finite number of digits.
68
69
    // In order for a float in radix `b` with a finite number of digits
70
    // to have a finite representation in radix `y`, `b` should divide
71
    // an integer power of `y`. This means for binary, all even radixes
72
    // have finite representations, and all odd ones do not.
73
    #[cfg(feature = "radix")]
74
    {
75
        if let Some(max_digits) = F::max_digits(format.radix()) {
76
            // Can use our finite number of digit algorithm.
77
            digit_comp::<F, FORMAT>(num, fp, sci_exp, max_digits)
78
        } else {
79
            // Fallback to infinite digits.
80
            byte_comp::<F, FORMAT>(num, fp, sci_exp)
81
        }
82
    }
83
84
    #[cfg(not(feature = "radix"))]
85
    {
86
        // Can use our finite number of digit algorithm.
87
14
        let max_digits = F::max_digits(format.radix()).unwrap();
88
14
        digit_comp::<F, FORMAT>(num, fp, sci_exp, max_digits)
89
    }
90
14
}
lexical_parse_float::slow::slow_radix::<f64, 0xa0000000000000000000000000c>
Line
Count
Source
49
14
pub fn slow_radix<F: RawFloat, const FORMAT: u128>(
50
14
    num: Number,
51
14
    fp: ExtendedFloat80,
52
14
) -> ExtendedFloat80 {
53
    // Ensure our preconditions are valid:
54
    //  1. The significant digits are not shifted into place.
55
14
    debug_assert!(fp.mant & (1 << 63) != 0, "number must be normalized");
56
57
14
    let format = NumberFormat::<{ FORMAT }> {};
58
59
    // This assumes the sign bit has already been parsed, and we're
60
    // starting with the integer digits, and the float format has been
61
    // correctly validated.
62
14
    let sci_exp = scientific_exponent::<FORMAT>(&num);
63
64
    // We have 3 major algorithms we use for this:
65
    //  1. An algorithm with a finite number of digits and a positive exponent.
66
    //  2. An algorithm with a finite number of digits and a negative exponent.
67
    //  3. A fallback algorithm with a non-finite number of digits.
68
69
    // In order for a float in radix `b` with a finite number of digits
70
    // to have a finite representation in radix `y`, `b` should divide
71
    // an integer power of `y`. This means for binary, all even radixes
72
    // have finite representations, and all odd ones do not.
73
    #[cfg(feature = "radix")]
74
    {
75
        if let Some(max_digits) = F::max_digits(format.radix()) {
76
            // Can use our finite number of digit algorithm.
77
            digit_comp::<F, FORMAT>(num, fp, sci_exp, max_digits)
78
        } else {
79
            // Fallback to infinite digits.
80
            byte_comp::<F, FORMAT>(num, fp, sci_exp)
81
        }
82
    }
83
84
    #[cfg(not(feature = "radix"))]
85
    {
86
        // Can use our finite number of digit algorithm.
87
14
        let max_digits = F::max_digits(format.radix()).unwrap();
88
14
        digit_comp::<F, FORMAT>(num, fp, sci_exp, max_digits)
89
    }
90
14
}
Unexecuted instantiation: lexical_parse_float::slow::slow_radix::<_, _>
91
92
/// Algorithm that generates the mantissa for a finite representation.
93
///
94
/// For a positive exponent relative to the significant digits, this
95
/// is just a multiplication by an exponent power. For a negative
96
/// exponent relative to the significant digits, we scale the real
97
/// digits to the theoretical digits for `b` and determine if we
98
/// need to round-up.
99
#[must_use]
100
#[inline(always)]
101
#[allow(clippy::cast_possible_wrap)] // reason = "the value range is [-324, 308]"
102
14
pub fn digit_comp<F: RawFloat, const FORMAT: u128>(
103
14
    num: Number,
104
14
    fp: ExtendedFloat80,
105
14
    sci_exp: i32,
106
14
    max_digits: usize,
107
14
) -> ExtendedFloat80 {
108
14
    let (bigmant, digits) = parse_mantissa::<FORMAT>(num, max_digits);
109
    // This can't underflow, since `digits` is at most `max_digits`.
110
14
    let exponent = sci_exp + 1 - digits as i32;
111
14
    if exponent >= 0 {
112
0
        positive_digit_comp::<F, FORMAT>(bigmant, exponent)
113
    } else {
114
14
        negative_digit_comp::<F, FORMAT>(bigmant, fp, exponent)
115
    }
116
14
}
lexical_parse_float::slow::digit_comp::<f64, 0xa0000000000000000000000000c>
Line
Count
Source
102
14
pub fn digit_comp<F: RawFloat, const FORMAT: u128>(
103
14
    num: Number,
104
14
    fp: ExtendedFloat80,
105
14
    sci_exp: i32,
106
14
    max_digits: usize,
107
14
) -> ExtendedFloat80 {
108
14
    let (bigmant, digits) = parse_mantissa::<FORMAT>(num, max_digits);
109
    // This can't underflow, since `digits` is at most `max_digits`.
110
14
    let exponent = sci_exp + 1 - digits as i32;
111
14
    if exponent >= 0 {
112
0
        positive_digit_comp::<F, FORMAT>(bigmant, exponent)
113
    } else {
114
14
        negative_digit_comp::<F, FORMAT>(bigmant, fp, exponent)
115
    }
116
14
}
Unexecuted instantiation: lexical_parse_float::slow::digit_comp::<_, _>
117
118
/// Generate the significant digits with a positive exponent relative to
119
/// mantissa.
120
#[must_use]
121
#[inline(always)]
122
#[allow(clippy::unwrap_used)] // reason = "none is a developer error"
123
#[allow(clippy::cast_possible_wrap)] // reason = "can't wrap in practice: max is ~1000 limbs"
124
#[allow(clippy::missing_inline_in_public_items)] // reason = "only public for testing"
125
0
pub fn positive_digit_comp<F: RawFloat, const FORMAT: u128>(
126
0
    mut bigmant: Bigint,
127
0
    exponent: i32,
128
0
) -> ExtendedFloat80 {
129
0
    let format = NumberFormat::<{ FORMAT }> {};
130
131
    // Simple, we just need to multiply by the power of the radix.
132
    // Now, we can calculate the mantissa and the exponent from this.
133
    // The binary exponent is the binary exponent for the mantissa
134
    // shifted to the hidden bit.
135
0
    bigmant.pow(format.radix(), exponent as u32).unwrap();
136
137
    // Get the exact representation of the float from the big integer.
138
    // hi64 checks **all** the remaining bits after the mantissa,
139
    // so it will check if **any** truncated digits exist.
140
0
    let (mant, is_truncated) = bigmant.hi64();
141
0
    let exp = bigmant.bit_length() as i32 - 64 + F::EXPONENT_BIAS;
142
0
    let mut fp = ExtendedFloat80 {
143
0
        mant,
144
0
        exp,
145
0
    };
146
147
    // Shift the digits into position and determine if we need to round-up.
148
0
    shared::round::<F, _>(&mut fp, |f, s| {
149
0
        shared::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| {
150
0
            is_above || (is_halfway && is_truncated) || (is_odd && is_halfway)
151
0
        });
Unexecuted instantiation: lexical_parse_float::slow::positive_digit_comp::<f64, 0xa0000000000000000000000000c>::{closure#0}::{closure#0}
Unexecuted instantiation: lexical_parse_float::slow::positive_digit_comp::<_, _>::{closure#0}::{closure#0}
152
0
    });
Unexecuted instantiation: lexical_parse_float::slow::positive_digit_comp::<f64, 0xa0000000000000000000000000c>::{closure#0}
Unexecuted instantiation: lexical_parse_float::slow::positive_digit_comp::<_, _>::{closure#0}
153
0
    fp
154
0
}
Unexecuted instantiation: lexical_parse_float::slow::positive_digit_comp::<f64, 0xa0000000000000000000000000c>
Unexecuted instantiation: lexical_parse_float::slow::positive_digit_comp::<_, _>
155
156
/// Generate the significant digits with a negative exponent relative to
157
/// mantissa.
158
///
159
/// This algorithm is quite simple: we have the significant digits `m1 * b^N1`,
160
/// where `m1` is the bigint mantissa, `b` is the radix, and `N1` is the radix
161
/// exponent. We then calculate the theoretical representation of `b+h`, which
162
/// is `m2 * 2^N2`, where `m2` is the bigint mantissa and `N2` is the binary
163
/// exponent. If we had infinite, efficient floating precision, this would be
164
/// equal to `m1 / b^-N1` and then compare it to `m2 * 2^N2`.
165
///
166
/// Since we cannot divide and keep precision, we must multiply the other:
167
/// if we want to do `m1 / b^-N1 >= m2 * 2^N2`, we can do
168
/// `m1 >= m2 * b^-N1 * 2^N2` Going to the decimal case, we can show and example
169
/// and simplify this further: `m1 >= m2 * 2^N2 * 10^-N1`. Since we can remove
170
/// a power-of-two, this is `m1 >= m2 * 2^(N2 - N1) * 5^-N1`. Therefore, if
171
/// `N2 - N1 > 0`, we need have `m1 >= m2 * 2^(N2 - N1) * 5^-N1`, otherwise,
172
/// we have `m1 * 2^(N1 - N2) >= m2 * 5^-N1`, where the resulting exponents
173
/// are all positive.
174
///
175
/// This allows us to compare both floats using integers efficiently
176
/// without any loss of precision.
177
#[must_use]
178
#[inline(always)]
179
#[allow(clippy::match_bool)] // reason = "simplifies documentation"
180
#[allow(clippy::unwrap_used)] // reason = "unwrap panics if a developer error"
181
#[allow(clippy::comparison_chain)] // reason = "logically different conditions for algorithm"
182
#[allow(clippy::missing_inline_in_public_items)] // reason = "only exposed for unittesting"
183
14
pub fn negative_digit_comp<F: RawFloat, const FORMAT: u128>(
184
14
    bigmant: Bigint,
185
14
    mut fp: ExtendedFloat80,
186
14
    exponent: i32,
187
14
) -> ExtendedFloat80 {
188
    // Ensure our preconditions are valid:
189
    //  1. The significant digits are not shifted into place.
190
14
    debug_assert!(fp.mant & (1 << 63) != 0, "the significant digits must be normalized");
191
192
14
    let format = NumberFormat::<FORMAT> {};
193
14
    let radix = format.radix();
194
195
    // Get the significant digits and radix exponent for the real digits.
196
14
    let mut real_digits = bigmant;
197
14
    let real_exp = exponent;
198
14
    debug_assert!(real_exp < 0, "algorithm only works with negative numbers");
199
200
    // Round down our extended-precision float and calculate `b`.
201
14
    let mut b = fp;
202
14
    shared::round::<F, _>(&mut b, shared::round_down);
203
14
    let b = extended_to_float::<F>(b);
204
205
    // Get the significant digits and the binary exponent for `b+h`.
206
14
    let theor = bh(b);
207
14
    let mut theor_digits = Bigint::from_u64(theor.mant);
208
14
    let theor_exp = theor.exp;
209
210
    // We need to scale the real digits and `b+h` digits to be the same
211
    // order. We currently have `real_exp`, in `radix`, that needs to be
212
    // shifted to `theor_digits` (since it is negative), and `theor_exp`
213
    // to either `theor_digits` or `real_digits` as a power of 2 (since it
214
    // may be positive or negative). Try to remove as many powers of 2
215
    // as possible. All values are relative to `theor_digits`, that is,
216
    // reflect the power you need to multiply `theor_digits` by.
217
14
    let (binary_exp, halfradix_exp, radix_exp) = match radix.is_even() {
218
        // Can remove a power-of-two.
219
        // Both are on opposite-sides of equation, can factor out a
220
        // power of two.
221
        //
222
        // Example: 10^-10, 2^-10   -> ( 0, 10, 0)
223
        // Example: 10^-10, 2^-15   -> (-5, 10, 0)
224
        // Example: 10^-10, 2^-5    -> ( 5, 10, 0)
225
        // Example: 10^-10, 2^5     -> (15, 10, 0)
226
14
        true => (theor_exp - real_exp, -real_exp, 0),
227
        // Cannot remove a power-of-two.
228
0
        false => (theor_exp, 0, -real_exp),
229
    };
230
231
14
    if halfradix_exp != 0 {
232
14
        theor_digits.pow(radix / 2, halfradix_exp as u32).unwrap();
233
14
    }
234
14
    if radix_exp != 0 {
235
0
        theor_digits.pow(radix, radix_exp as u32).unwrap();
236
14
    }
237
14
    if binary_exp > 0 {
238
11
        theor_digits.pow(2, binary_exp as u32).unwrap();
239
11
    } else if binary_exp < 0 {
240
3
        real_digits.pow(2, (-binary_exp) as u32).unwrap();
241
3
    }
242
243
    // Compare our theoretical and real digits and round nearest, tie even.
244
14
    let ord = real_digits.data.cmp(&theor_digits.data);
245
14
    shared::round::<F, _>(&mut fp, |f, s| {
246
14
        shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
247
            // Can ignore `is_halfway` and `is_above`, since those were
248
            // calculates using less significant digits.
249
0
            match ord {
250
11
                cmp::Ordering::Greater => true,
251
3
                cmp::Ordering::Less => false,
252
0
                cmp::Ordering::Equal if is_odd => true,
253
0
                cmp::Ordering::Equal => false,
254
            }
255
14
        });
lexical_parse_float::slow::negative_digit_comp::<f64, 0xa0000000000000000000000000c>::{closure#0}::{closure#0}
Line
Count
Source
246
14
        shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
247
            // Can ignore `is_halfway` and `is_above`, since those were
248
            // calculates using less significant digits.
249
0
            match ord {
250
11
                cmp::Ordering::Greater => true,
251
3
                cmp::Ordering::Less => false,
252
0
                cmp::Ordering::Equal if is_odd => true,
253
0
                cmp::Ordering::Equal => false,
254
            }
255
14
        });
Unexecuted instantiation: lexical_parse_float::slow::negative_digit_comp::<_, _>::{closure#0}::{closure#0}
256
14
    });
lexical_parse_float::slow::negative_digit_comp::<f64, 0xa0000000000000000000000000c>::{closure#0}
Line
Count
Source
245
14
    shared::round::<F, _>(&mut fp, |f, s| {
246
14
        shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
247
            // Can ignore `is_halfway` and `is_above`, since those were
248
            // calculates using less significant digits.
249
            match ord {
250
                cmp::Ordering::Greater => true,
251
                cmp::Ordering::Less => false,
252
                cmp::Ordering::Equal if is_odd => true,
253
                cmp::Ordering::Equal => false,
254
            }
255
        });
256
14
    });
Unexecuted instantiation: lexical_parse_float::slow::negative_digit_comp::<_, _>::{closure#0}
257
14
    fp
258
14
}
lexical_parse_float::slow::negative_digit_comp::<f64, 0xa0000000000000000000000000c>
Line
Count
Source
183
14
pub fn negative_digit_comp<F: RawFloat, const FORMAT: u128>(
184
14
    bigmant: Bigint,
185
14
    mut fp: ExtendedFloat80,
186
14
    exponent: i32,
187
14
) -> ExtendedFloat80 {
188
    // Ensure our preconditions are valid:
189
    //  1. The significant digits are not shifted into place.
190
14
    debug_assert!(fp.mant & (1 << 63) != 0, "the significant digits must be normalized");
191
192
14
    let format = NumberFormat::<FORMAT> {};
193
14
    let radix = format.radix();
194
195
    // Get the significant digits and radix exponent for the real digits.
196
14
    let mut real_digits = bigmant;
197
14
    let real_exp = exponent;
198
14
    debug_assert!(real_exp < 0, "algorithm only works with negative numbers");
199
200
    // Round down our extended-precision float and calculate `b`.
201
14
    let mut b = fp;
202
14
    shared::round::<F, _>(&mut b, shared::round_down);
203
14
    let b = extended_to_float::<F>(b);
204
205
    // Get the significant digits and the binary exponent for `b+h`.
206
14
    let theor = bh(b);
207
14
    let mut theor_digits = Bigint::from_u64(theor.mant);
208
14
    let theor_exp = theor.exp;
209
210
    // We need to scale the real digits and `b+h` digits to be the same
211
    // order. We currently have `real_exp`, in `radix`, that needs to be
212
    // shifted to `theor_digits` (since it is negative), and `theor_exp`
213
    // to either `theor_digits` or `real_digits` as a power of 2 (since it
214
    // may be positive or negative). Try to remove as many powers of 2
215
    // as possible. All values are relative to `theor_digits`, that is,
216
    // reflect the power you need to multiply `theor_digits` by.
217
14
    let (binary_exp, halfradix_exp, radix_exp) = match radix.is_even() {
218
        // Can remove a power-of-two.
219
        // Both are on opposite-sides of equation, can factor out a
220
        // power of two.
221
        //
222
        // Example: 10^-10, 2^-10   -> ( 0, 10, 0)
223
        // Example: 10^-10, 2^-15   -> (-5, 10, 0)
224
        // Example: 10^-10, 2^-5    -> ( 5, 10, 0)
225
        // Example: 10^-10, 2^5     -> (15, 10, 0)
226
14
        true => (theor_exp - real_exp, -real_exp, 0),
227
        // Cannot remove a power-of-two.
228
0
        false => (theor_exp, 0, -real_exp),
229
    };
230
231
14
    if halfradix_exp != 0 {
232
14
        theor_digits.pow(radix / 2, halfradix_exp as u32).unwrap();
233
14
    }
234
14
    if radix_exp != 0 {
235
0
        theor_digits.pow(radix, radix_exp as u32).unwrap();
236
14
    }
237
14
    if binary_exp > 0 {
238
11
        theor_digits.pow(2, binary_exp as u32).unwrap();
239
11
    } else if binary_exp < 0 {
240
3
        real_digits.pow(2, (-binary_exp) as u32).unwrap();
241
3
    }
242
243
    // Compare our theoretical and real digits and round nearest, tie even.
244
14
    let ord = real_digits.data.cmp(&theor_digits.data);
245
14
    shared::round::<F, _>(&mut fp, |f, s| {
246
        shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
247
            // Can ignore `is_halfway` and `is_above`, since those were
248
            // calculates using less significant digits.
249
            match ord {
250
                cmp::Ordering::Greater => true,
251
                cmp::Ordering::Less => false,
252
                cmp::Ordering::Equal if is_odd => true,
253
                cmp::Ordering::Equal => false,
254
            }
255
        });
256
    });
257
14
    fp
258
14
}
Unexecuted instantiation: lexical_parse_float::slow::negative_digit_comp::<_, _>
259
260
/// Try to parse 8 digits at a time.
261
///
262
/// - `format` - The numerical format specification as a packed 128-bit integer
263
/// - `iter` - An iterator over all bytes in the buffer
264
/// - `value` - The currently parsed value.
265
/// - `count` - The total number of parsed digits
266
/// - `counter` - The number of parsed digits since creating the current u32
267
/// - `step` - The maximum number of digits for the radix that can fit in a u32.
268
/// - `max_digits` - The maximum number of digits that can affect floating-point
269
///   rounding.
270
#[cfg(not(feature = "compact"))]
271
macro_rules! try_parse_8digits {
272
    (
273
        $format:ident,
274
        $iter:ident,
275
        $value:ident,
276
        $count:ident,
277
        $counter:ident,
278
        $step:ident,
279
        $max_digits:ident
280
    ) => {{
281
        let format = NumberFormat::<$format> {};
282
        let radix = format.radix() as Limb;
283
284
        // Try 8-digit optimizations.
285
        if can_try_parse_multidigit!($iter, radix) {
286
            debug_assert!(radix < 16);
287
            let radix8 = format.radix8() as Limb;
288
            while $step - $counter >= 8 && $max_digits - $count >= 8 {
289
                if let Some(v) = algorithm::try_parse_8digits::<Limb, _, FORMAT>(&mut $iter) {
290
                    $value = $value.wrapping_mul(radix8).wrapping_add(v);
291
                    $counter += 8;
292
                    $count += 8;
293
                } else {
294
                    break;
295
                }
296
            }
297
        }
298
    }};
299
}
300
301
/// Add a digit to the temporary value.
302
///
303
/// - `c` - The character to convert to a digit.
304
/// - `value` - The currently parsed value.
305
/// - `count` - The total number of parsed digits
306
/// - `counter` - The number of parsed digits since creating the current u32
307
macro_rules! add_digit {
308
    ($c:ident, $radix:ident, $value:ident, $counter:ident, $count:ident) => {{
309
        let digit = char_to_valid_digit_const($c, $radix);
310
        $value *= $radix as Limb;
311
        $value += digit as Limb;
312
313
        // Increment our counters.
314
        $counter += 1;
315
        $count += 1;
316
    }};
317
}
318
319
/// Add a temporary value to our mantissa.
320
///
321
/// - `format` - The numerical format specification as a packed 128-bit integer
322
/// - `result` - The big integer,
323
/// - `power` - The power to scale the big integer by.
324
/// - `value` - The value to add to the big integer,
325
/// - `counter` - The number of parsed digits since creating the current u32
326
macro_rules! add_temporary {
327
    // Multiply by the small power and add the native value.
328
    (@mul $result:ident, $power:expr, $value:expr) => {
329
        $result.data.mul_small($power).unwrap();
330
        $result.data.add_small($value).unwrap();
331
    };
332
333
    // Add a temporary where we won't read the counter results internally.
334
    (@end $format:ident, $result:ident, $counter:ident, $value:ident) => {
335
        if $counter != 0 {
336
            let small_power = f64::int_pow_fast_path($counter, $format.radix());
337
            add_temporary!(@mul $result, small_power as Limb, $value);
338
        }
339
    };
340
341
    // Add the maximum native value.
342
    (@max $format:ident, $result:ident, $counter:ident, $value:ident, $max:ident) => {
343
        add_temporary!(@mul $result, $max, $value);
344
        $counter = 0;
345
        $value = 0;
346
    };
347
}
348
349
/// Round-up a truncated value.
350
///
351
/// - `format` - The numerical format specification as a packed 128-bit integer
352
/// - `result` - The big integer,
353
/// - `count` - The total number of parsed digits
354
macro_rules! round_up_truncated {
355
    ($format:ident, $result:ident, $count:ident) => {{
356
        // Need to round-up.
357
        // Can't just add 1, since this can accidentally round-up
358
        // values to a halfway point, which can cause invalid results.
359
        add_temporary!(@mul $result, $format.radix() as Limb, 1);
360
        $count += 1;
361
    }};
362
}
363
364
/// Check and round-up the fraction if any non-zero digits exist.
365
///
366
/// - `format` - The numerical format specification as a packed 128-bit integer
367
/// - `iter` - An iterator over all bytes in the buffer
368
/// - `result` - The big integer,
369
/// - `count` - The total number of parsed digits
370
macro_rules! round_up_nonzero {
371
    ($format:ident, $iter:expr, $result:ident, $count:ident) => {{
372
        // NOTE: All digits must already be valid.
373
        let mut iter = $iter;
374
375
        // First try reading 8-digits at a time.
376
        if iter.is_contiguous() {
377
            while let Some(value) = iter.peek_u64() {
378
                // SAFETY: safe since we have at least 8 bytes in the buffer.
379
                unsafe { iter.step_by_unchecked(8) };
380
                if value != 0x3030_3030_3030_3030 {
381
                    // Have non-zero digits, exit early.
382
                    round_up_truncated!($format, $result, $count);
383
                    return ($result, $count);
384
                }
385
            }
386
        }
387
388
        for &digit in iter {
389
            if digit != b'0' {
390
                round_up_truncated!($format, $result, $count);
391
                return ($result, $count);
392
            }
393
        }
394
    }};
395
}
396
397
/// Parse the full mantissa into a big integer.
398
///
399
/// Returns the parsed mantissa and the number of digits in the mantissa.
400
/// The max digits is the maximum number of digits plus one.
401
#[must_use]
402
#[allow(clippy::cognitive_complexity)] // reason = "complexity broken into macros"
403
#[allow(clippy::missing_inline_in_public_items)] // reason = "only public for testing"
404
14
pub fn parse_mantissa<const FORMAT: u128>(num: Number, max_digits: usize) -> (Bigint, usize) {
405
14
    let format = NumberFormat::<FORMAT> {};
406
14
    let radix = format.radix();
407
408
    // Iteratively process all the data in the mantissa.
409
    // We do this via small, intermediate values which once we reach
410
    // the maximum number of digits we can process without overflow,
411
    // we add the temporary to the big integer.
412
14
    let mut counter: usize = 0;
413
14
    let mut count: usize = 0;
414
14
    let mut value: Limb = 0;
415
14
    let mut result = Bigint::new();
416
417
    // Now use our pre-computed small powers iteratively.
418
14
    let step = if Limb::BITS == 32 {
419
0
        u32_power_limit(format.radix())
420
    } else {
421
14
        u64_power_limit(format.radix())
422
    } as usize;
423
14
    let max_native = (format.radix() as Limb).pow(step as u32);
424
425
    // Process the integer digits.
426
14
    let mut integer = num.integer.bytes::<FORMAT>();
427
14
    let mut integer_iter = integer.integer_iter();
428
14
    integer_iter.skip_zeros();
429
    'integer: loop {
430
        #[cfg(not(feature = "compact"))]
431
21
        try_parse_8digits!(FORMAT, integer_iter, value, count, counter, step, max_digits);
432
433
        // Parse a digit at a time, until we reach step.
434
71
        while counter < step && count < max_digits {
435
64
            if let Some(&c) = integer_iter.next() {
436
50
                add_digit!(c, radix, value, counter, count);
437
50
            } else {
438
14
                break 'integer;
439
            }
440
        }
441
442
        // Check if we've exhausted our max digits.
443
7
        if count == max_digits {
444
            // Need to check if we're truncated, and round-up accordingly.
445
            // SAFETY: safe since `counter <= step`.
446
0
            add_temporary!(@end format, result, counter, value);
447
0
            round_up_nonzero!(format, integer_iter, result, count);
448
0
            if let Some(fraction) = num.fraction {
449
0
                let mut fraction = fraction.bytes::<FORMAT>();
450
0
                round_up_nonzero!(format, fraction.fraction_iter(), result, count);
451
0
            }
452
0
            return (result, count);
453
7
        } else {
454
7
            // Add our temporary from the loop.
455
7
            // SAFETY: safe since `counter <= step`.
456
7
            add_temporary!(@max format, result, counter, value, max_native);
457
7
        }
458
    }
459
460
    // Process the fraction digits.
461
14
    if let Some(fraction) = num.fraction {
462
14
        let mut fraction = fraction.bytes::<FORMAT>();
463
14
        let mut fraction_iter = fraction.integer_iter();
464
14
        if count == 0 {
465
0
            // No digits added yet, can skip leading fraction zeros too.
466
0
            fraction_iter.skip_zeros();
467
14
        }
468
        'fraction: loop {
469
            #[cfg(not(feature = "compact"))]
470
23
            try_parse_8digits!(FORMAT, fraction_iter, value, count, counter, step, max_digits);
471
472
            // Parse a digit at a time, until we reach step.
473
109
            while counter < step && count < max_digits {
474
100
                if let Some(&c) = fraction_iter.next() {
475
86
                    add_digit!(c, radix, value, counter, count);
476
86
                } else {
477
14
                    break 'fraction;
478
                }
479
            }
480
481
            // Check if we've exhausted our max digits.
482
9
            if count == max_digits {
483
                // SAFETY: safe since `counter <= step`.
484
0
                add_temporary!(@end format, result, counter, value);
485
0
                round_up_nonzero!(format, fraction_iter, result, count);
486
0
                return (result, count);
487
9
            } else {
488
9
                // Add our temporary from the loop.
489
9
                // SAFETY: safe since `counter <= step`.
490
9
                add_temporary!(@max format, result, counter, value, max_native);
491
9
            }
492
        }
493
0
    }
494
495
    // We will always have a remainder, as long as we entered the loop
496
    // once, or counter % step is 0.
497
    // SAFETY: safe since `counter <= step`.
498
14
    add_temporary!(@end format, result, counter, value);
499
500
14
    (result, count)
501
14
}
lexical_parse_float::slow::parse_mantissa::<0xa0000000000000000000000000c>
Line
Count
Source
404
14
pub fn parse_mantissa<const FORMAT: u128>(num: Number, max_digits: usize) -> (Bigint, usize) {
405
14
    let format = NumberFormat::<FORMAT> {};
406
14
    let radix = format.radix();
407
408
    // Iteratively process all the data in the mantissa.
409
    // We do this via small, intermediate values which once we reach
410
    // the maximum number of digits we can process without overflow,
411
    // we add the temporary to the big integer.
412
14
    let mut counter: usize = 0;
413
14
    let mut count: usize = 0;
414
14
    let mut value: Limb = 0;
415
14
    let mut result = Bigint::new();
416
417
    // Now use our pre-computed small powers iteratively.
418
14
    let step = if Limb::BITS == 32 {
419
0
        u32_power_limit(format.radix())
420
    } else {
421
14
        u64_power_limit(format.radix())
422
    } as usize;
423
14
    let max_native = (format.radix() as Limb).pow(step as u32);
424
425
    // Process the integer digits.
426
14
    let mut integer = num.integer.bytes::<FORMAT>();
427
14
    let mut integer_iter = integer.integer_iter();
428
14
    integer_iter.skip_zeros();
429
    'integer: loop {
430
        #[cfg(not(feature = "compact"))]
431
21
        try_parse_8digits!(FORMAT, integer_iter, value, count, counter, step, max_digits);
432
433
        // Parse a digit at a time, until we reach step.
434
71
        while counter < step && count < max_digits {
435
64
            if let Some(&c) = integer_iter.next() {
436
50
                add_digit!(c, radix, value, counter, count);
437
50
            } else {
438
14
                break 'integer;
439
            }
440
        }
441
442
        // Check if we've exhausted our max digits.
443
7
        if count == max_digits {
444
            // Need to check if we're truncated, and round-up accordingly.
445
            // SAFETY: safe since `counter <= step`.
446
0
            add_temporary!(@end format, result, counter, value);
447
0
            round_up_nonzero!(format, integer_iter, result, count);
448
0
            if let Some(fraction) = num.fraction {
449
0
                let mut fraction = fraction.bytes::<FORMAT>();
450
0
                round_up_nonzero!(format, fraction.fraction_iter(), result, count);
451
0
            }
452
0
            return (result, count);
453
7
        } else {
454
7
            // Add our temporary from the loop.
455
7
            // SAFETY: safe since `counter <= step`.
456
7
            add_temporary!(@max format, result, counter, value, max_native);
457
7
        }
458
    }
459
460
    // Process the fraction digits.
461
14
    if let Some(fraction) = num.fraction {
462
14
        let mut fraction = fraction.bytes::<FORMAT>();
463
14
        let mut fraction_iter = fraction.integer_iter();
464
14
        if count == 0 {
465
0
            // No digits added yet, can skip leading fraction zeros too.
466
0
            fraction_iter.skip_zeros();
467
14
        }
468
        'fraction: loop {
469
            #[cfg(not(feature = "compact"))]
470
23
            try_parse_8digits!(FORMAT, fraction_iter, value, count, counter, step, max_digits);
471
472
            // Parse a digit at a time, until we reach step.
473
109
            while counter < step && count < max_digits {
474
100
                if let Some(&c) = fraction_iter.next() {
475
86
                    add_digit!(c, radix, value, counter, count);
476
86
                } else {
477
14
                    break 'fraction;
478
                }
479
            }
480
481
            // Check if we've exhausted our max digits.
482
9
            if count == max_digits {
483
                // SAFETY: safe since `counter <= step`.
484
0
                add_temporary!(@end format, result, counter, value);
485
0
                round_up_nonzero!(format, fraction_iter, result, count);
486
0
                return (result, count);
487
9
            } else {
488
9
                // Add our temporary from the loop.
489
9
                // SAFETY: safe since `counter <= step`.
490
9
                add_temporary!(@max format, result, counter, value, max_native);
491
9
            }
492
        }
493
0
    }
494
495
    // We will always have a remainder, as long as we entered the loop
496
    // once, or counter % step is 0.
497
    // SAFETY: safe since `counter <= step`.
498
14
    add_temporary!(@end format, result, counter, value);
499
500
14
    (result, count)
501
14
}
Unexecuted instantiation: lexical_parse_float::slow::parse_mantissa::<_>
502
503
/// Compare actual integer digits to the theoretical digits.
504
///
505
/// - `iter` - An iterator over all bytes in the buffer
506
/// - `num` - The actual digits of the real floating point number.
507
/// - `den` - The theoretical digits created by `b+h` to determine if `b` or
508
///   `b+1`
509
#[cfg(feature = "radix")]
510
macro_rules! integer_compare {
511
    ($iter:ident, $num:ident, $den:ident, $radix:ident) => {{
512
        // Compare the integer digits.
513
        while !$num.data.is_empty() {
514
            // All digits **must** be valid.
515
            let actual = match $iter.next() {
516
                Some(&v) => v,
517
                // Could have hit the decimal point.
518
                _ => break,
519
            };
520
            let rem = $num.data.quorem(&$den.data) as u32;
521
            let expected = digit_to_char_const(rem, $radix);
522
            $num.data.mul_small($radix as Limb).unwrap();
523
            if actual < expected {
524
                return cmp::Ordering::Less;
525
            } else if actual > expected {
526
                return cmp::Ordering::Greater;
527
            }
528
        }
529
530
        // Still have integer digits, check if any are non-zero.
531
        if $num.data.is_empty() {
532
            for &digit in $iter {
533
                if digit != b'0' {
534
                    return cmp::Ordering::Greater;
535
                }
536
            }
537
        }
538
    }};
539
}
540
541
/// Compare actual fraction digits to the theoretical digits.
542
///
543
/// - `iter` - An iterator over all bytes in the buffer
544
/// - `num` - The actual digits of the real floating point number.
545
/// - `den` - The theoretical digits created by `b+h` to determine if `b` or
546
///   `b+1`
547
#[cfg(feature = "radix")]
548
macro_rules! fraction_compare {
549
    ($iter:ident, $num:ident, $den:ident, $radix:ident) => {{
550
        // Compare the fraction digits.
551
        // We can only be here if we hit a decimal point.
552
        while !$num.data.is_empty() {
553
            // All digits **must** be valid.
554
            let actual = match $iter.next() {
555
                Some(&v) => v,
556
                // No more actual digits, or hit the exponent.
557
                _ => return cmp::Ordering::Less,
558
            };
559
            let rem = $num.data.quorem(&$den.data) as u32;
560
            let expected = digit_to_char_const(rem, $radix);
561
            $num.data.mul_small($radix as Limb).unwrap();
562
            if actual < expected {
563
                return cmp::Ordering::Less;
564
            } else if actual > expected {
565
                return cmp::Ordering::Greater;
566
            }
567
        }
568
569
        // Still have fraction digits, check if any are non-zero.
570
        for &digit in $iter {
571
            if digit != b'0' {
572
                return cmp::Ordering::Greater;
573
            }
574
        }
575
    }};
576
}
577
578
/// Compare theoretical digits to halfway point from theoretical digits.
579
///
580
/// Generates a float representing the halfway point, and generates
581
/// theoretical digits as bytes, and compares the generated digits to
582
/// the actual input.
583
///
584
/// Compares the known string to theoretical digits generated on the
585
/// fly for `b+h`, where a string representation of a float is between
586
/// `b` and `b+u`, where `b+u` is 1 unit in the least-precision. Therefore,
587
/// the string must be close to `b+h`.
588
///
589
/// Adapted from "Bigcomp: Deciding Truncated, Near Halfway Conversions",
590
/// available [here](https://www.exploringbinary.com/bigcomp-deciding-truncated-near-halfway-conversions/).
591
#[cfg(feature = "radix")]
592
#[allow(clippy::unwrap_used)] // reason = "none is a developer error due to shl overflow"
593
#[allow(clippy::comparison_chain)] // reason = "logically different conditions for algorithm"
594
pub fn byte_comp<F: RawFloat, const FORMAT: u128>(
595
    number: Number,
596
    mut fp: ExtendedFloat80,
597
    sci_exp: i32,
598
) -> ExtendedFloat80 {
599
    // Ensure our preconditions are valid:
600
    //  1. The significant digits are not shifted into place.
601
    debug_assert!(fp.mant & (1 << 63) != 0);
602
603
    let format = NumberFormat::<FORMAT> {};
604
605
    // Round down our extended-precision float and calculate `b`.
606
    let mut b = fp;
607
    shared::round::<F, _>(&mut b, shared::round_down);
608
    let b = extended_to_float::<F>(b);
609
610
    // Calculate `b+h` to create a ratio for our theoretical digits.
611
    let theor = Bigfloat::from_float(bh::<F>(b));
612
613
    // Now, create a scaling factor for the digit count.
614
    let mut factor = Bigfloat::from_u32(1);
615
    factor.pow(format.radix(), sci_exp.unsigned_abs()).unwrap();
616
    let mut num: Bigfloat;
617
    let mut den: Bigfloat;
618
619
    if sci_exp < 0 {
620
        // Need to have the basen factor be the numerator, and the `fp`
621
        // be the denominator. Since we assumed that `theor` was the numerator,
622
        // if it's the denominator, we need to multiply it into the numerator.
623
        num = factor;
624
        num.data *= &theor.data;
625
        den = Bigfloat::from_u32(1);
626
        den.exp = -theor.exp;
627
    } else {
628
        num = theor;
629
        den = factor;
630
    }
631
632
    // Scale the denominator so it has the number of bits
633
    // in the radix as the number of leading zeros.
634
    let wlz = integral_binary_factor(format.radix());
635
    let nlz = den.leading_zeros().wrapping_sub(wlz) & (32 - 1);
636
    if nlz != 0 {
637
        den.shl_bits(nlz as usize).unwrap();
638
        den.exp -= nlz as i32;
639
    }
640
641
    // Need to scale the numerator or denominator to the same value.
642
    // We don't want to shift the denominator, so...
643
    let diff = den.exp - num.exp;
644
    let shift = diff.unsigned_abs() as usize;
645
    if diff < 0 {
646
        // Need to shift the numerator left.
647
        num.shl(shift).unwrap();
648
        num.exp -= shift as i32;
649
    } else if diff > 0 {
650
        // Need to shift denominator left, go by a power of Limb::BITS.
651
        // After this, the numerator will be non-normalized, and the
652
        // denominator will be normalized. We need to add one to the
653
        // quotient,since we're calculating the ceiling of the divmod.
654
        let (q, r) = shift.ceil_divmod(Limb::BITS as usize);
655
        let r = -r;
656
        if r != 0 {
657
            num.shl_bits(r as usize).unwrap();
658
            num.exp -= r;
659
        }
660
        if q != 0 {
661
            den.shl_limbs(q).unwrap();
662
            den.exp -= Limb::BITS as i32 * q as i32;
663
        }
664
    }
665
666
    // Compare our theoretical and real digits and round nearest, tie even.
667
    let ord = compare_bytes::<FORMAT>(number, num, den);
668
    shared::round::<F, _>(&mut fp, |f, s| {
669
        shared::round_nearest_tie_even(f, s, |is_odd, _, _| {
670
            // Can ignore `is_halfway` and `is_above`, since those were
671
            // calculates using less significant digits.
672
            match ord {
673
                cmp::Ordering::Greater => true,
674
                cmp::Ordering::Less => false,
675
                cmp::Ordering::Equal if is_odd => true,
676
                cmp::Ordering::Equal => false,
677
            }
678
        });
679
    });
680
    fp
681
}
682
683
/// Compare digits between the generated values the ratio and the actual view.
684
///
685
/// - `number` - The representation of the float as a big number, with the
686
///   parsed digits.
687
/// - `num` - The actual digits of the real floating point number.
688
/// - `den` - The theoretical digits created by `b+h` to determine if `b` or
689
///   `b+1`
690
#[cfg(feature = "radix")]
691
#[allow(clippy::unwrap_used)] // reason = "none is a developer error due to a missing fraction"
692
pub fn compare_bytes<const FORMAT: u128>(
693
    number: Number,
694
    mut num: Bigfloat,
695
    den: Bigfloat,
696
) -> cmp::Ordering {
697
    let format = NumberFormat::<FORMAT> {};
698
    let radix = format.radix();
699
700
    // Now need to compare the theoretical digits. First, I need to trim
701
    // any leading zeros, and will also need to ignore trailing ones.
702
    let mut integer = number.integer.bytes::<{ FORMAT }>();
703
    let mut integer_iter = integer.integer_iter();
704
    integer_iter.skip_zeros();
705
    if integer_iter.is_buffer_empty() {
706
        // Cannot be empty, since we must have at least **some** significant digits.
707
        let mut fraction = number.fraction.unwrap().bytes::<{ FORMAT }>();
708
        let mut fraction_iter = fraction.fraction_iter();
709
        fraction_iter.skip_zeros();
710
        fraction_compare!(fraction_iter, num, den, radix);
711
    } else {
712
        integer_compare!(integer_iter, num, den, radix);
713
        if let Some(fraction) = number.fraction {
714
            let mut fraction = fraction.bytes::<{ FORMAT }>();
715
            let mut fraction_iter = fraction.fraction_iter();
716
            fraction_compare!(fraction_iter, num, den, radix);
717
        } else if !num.data.is_empty() {
718
            // We had more theoretical digits, but no more actual digits.
719
            return cmp::Ordering::Less;
720
        }
721
    }
722
723
    // Exhausted both, must be equal.
724
    cmp::Ordering::Equal
725
}
726
727
// SCALING
728
// -------
729
730
/// Calculate the scientific exponent from a `Number` value.
731
/// Any other attempts would require slowdowns for faster algorithms.
732
#[must_use]
733
#[inline(always)]
734
14
pub fn scientific_exponent<const FORMAT: u128>(num: &Number) -> i32 {
735
    // This has the significant digits and exponent relative to those
736
    // digits: therefore, we just need to scale to mantissa to `[1, radix)`.
737
    // This doesn't need to be very fast.
738
14
    let format = NumberFormat::<FORMAT> {};
739
740
    // Use power reduction to make this faster: we need at least
741
    // `F::MANTISSA_SIZE` bits, so we must have at least radix^4 digits.
742
    // IF we're using base 3, we can have at most 11 divisions, and
743
    // base 36, at most ~4. So, this is reasonably efficient.
744
14
    let radix = format.radix() as u64;
745
14
    let radix2 = radix * radix;
746
14
    let radix4 = radix2 * radix2;
747
14
    let mut mantissa = num.mantissa;
748
14
    let mut exponent = num.exponent;
749
70
    while mantissa >= radix4 {
750
56
        mantissa /= radix4;
751
56
        exponent += 4;
752
56
    }
753
28
    while mantissa >= radix2 {
754
14
        mantissa /= radix2;
755
14
        exponent += 2;
756
14
    }
757
14
    while mantissa >= radix {
758
0
        mantissa /= radix;
759
0
        exponent += 1;
760
0
    }
761
14
    exponent as i32
762
14
}
lexical_parse_float::slow::scientific_exponent::<0xa0000000000000000000000000c>
Line
Count
Source
734
14
pub fn scientific_exponent<const FORMAT: u128>(num: &Number) -> i32 {
735
    // This has the significant digits and exponent relative to those
736
    // digits: therefore, we just need to scale to mantissa to `[1, radix)`.
737
    // This doesn't need to be very fast.
738
14
    let format = NumberFormat::<FORMAT> {};
739
740
    // Use power reduction to make this faster: we need at least
741
    // `F::MANTISSA_SIZE` bits, so we must have at least radix^4 digits.
742
    // IF we're using base 3, we can have at most 11 divisions, and
743
    // base 36, at most ~4. So, this is reasonably efficient.
744
14
    let radix = format.radix() as u64;
745
14
    let radix2 = radix * radix;
746
14
    let radix4 = radix2 * radix2;
747
14
    let mut mantissa = num.mantissa;
748
14
    let mut exponent = num.exponent;
749
70
    while mantissa >= radix4 {
750
56
        mantissa /= radix4;
751
56
        exponent += 4;
752
56
    }
753
28
    while mantissa >= radix2 {
754
14
        mantissa /= radix2;
755
14
        exponent += 2;
756
14
    }
757
14
    while mantissa >= radix {
758
0
        mantissa /= radix;
759
0
        exponent += 1;
760
0
    }
761
14
    exponent as i32
762
14
}
Unexecuted instantiation: lexical_parse_float::slow::scientific_exponent::<_>
763
764
/// Calculate `b` from a a representation of `b` as a float.
765
#[must_use]
766
#[inline(always)]
767
14
pub fn b<F: RawFloat>(float: F) -> ExtendedFloat80 {
768
14
    ExtendedFloat80 {
769
14
        mant: float.mantissa().as_u64(),
770
14
        exp: float.exponent(),
771
14
    }
772
14
}
lexical_parse_float::slow::b::<f64>
Line
Count
Source
767
14
pub fn b<F: RawFloat>(float: F) -> ExtendedFloat80 {
768
14
    ExtendedFloat80 {
769
14
        mant: float.mantissa().as_u64(),
770
14
        exp: float.exponent(),
771
14
    }
772
14
}
Unexecuted instantiation: lexical_parse_float::slow::b::<_>
773
774
/// Calculate `b+h` from a a representation of `b` as a float.
775
#[must_use]
776
#[inline(always)]
777
14
pub fn bh<F: RawFloat>(float: F) -> ExtendedFloat80 {
778
14
    let fp = b(float);
779
14
    ExtendedFloat80 {
780
14
        mant: (fp.mant << 1) + 1,
781
14
        exp: fp.exp - 1,
782
14
    }
783
14
}
lexical_parse_float::slow::bh::<f64>
Line
Count
Source
777
14
pub fn bh<F: RawFloat>(float: F) -> ExtendedFloat80 {
778
14
    let fp = b(float);
779
14
    ExtendedFloat80 {
780
14
        mant: (fp.mant << 1) + 1,
781
14
        exp: fp.exp - 1,
782
14
    }
783
14
}
Unexecuted instantiation: lexical_parse_float::slow::bh::<_>
784
785
// NOTE: There will never be binary factors here.
786
787
/// Calculate the integral ceiling of the binary factor from a basen number.
788
#[must_use]
789
#[inline(always)]
790
#[cfg(feature = "radix")]
791
pub const fn integral_binary_factor(radix: u32) -> u32 {
792
    match radix {
793
        3 => 2,
794
        5 => 3,
795
        6 => 3,
796
        7 => 3,
797
        9 => 4,
798
        10 => 4,
799
        11 => 4,
800
        12 => 4,
801
        13 => 4,
802
        14 => 4,
803
        15 => 4,
804
        17 => 5,
805
        18 => 5,
806
        19 => 5,
807
        20 => 5,
808
        21 => 5,
809
        22 => 5,
810
        23 => 5,
811
        24 => 5,
812
        25 => 5,
813
        26 => 5,
814
        27 => 5,
815
        28 => 5,
816
        29 => 5,
817
        30 => 5,
818
        31 => 5,
819
        33 => 6,
820
        34 => 6,
821
        35 => 6,
822
        36 => 6,
823
        // Invalid radix
824
        _ => 0,
825
    }
826
}
827
828
/// Calculate the integral ceiling of the binary factor from a basen number.
829
#[must_use]
830
#[inline(always)]
831
#[cfg(not(feature = "radix"))]
832
0
pub const fn integral_binary_factor(radix: u32) -> u32 {
833
0
    match radix {
834
0
        10 => 4,
835
        // Invalid radix
836
0
        _ => 0,
837
    }
838
0
}