mesh_loader/utils/float/
mod.rs

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
// Rust port of fast_float's float parser.
//
// Adapted from core::num::dec2flt with partial parsing support added, and very tricky and unsafe x87 FPU-related hack removed.
//
// Source: https://github.com/rust-lang/rust/tree/1.80.0/library/core/src/num/dec2flt
//
// Copyright & License of the original code:
// - https://github.com/rust-lang/rust/blob/1.80.0/COPYRIGHT
// - https://github.com/rust-lang/rust/blob/1.80.0/LICENSE-APACHE
// - https://github.com/rust-lang/rust/blob/1.80.0/LICENSE-MIT
//
// # References
//
// - Daniel Lemire, Number Parsing at a Gigabyte per Second, Software: Practice and Experience 51 (8), 2021.
//   https://arxiv.org/abs/2101.11408
// - https://github.com/fastfloat/fast_float

#![allow(
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::items_after_statements,
    clippy::module_inception,
    clippy::redundant_else
)]

pub(crate) mod common;
mod decimal;
mod float;
mod lemire;
mod number;
pub(crate) mod parse;
mod slow;
mod table;

use self::{
    common::{BiasedFp, ByteSlice},
    float::RawFloat,
    lemire::compute_float,
    parse::{parse_inf_nan, parse_partial_number},
    slow::parse_long_mantissa,
};

#[inline]
pub fn parse<T: Float>(bytes: &[u8]) -> Option<T> {
    T::parse(bytes)
}

#[inline]
pub fn parse_partial<T: Float>(bytes: &[u8]) -> Option<(T, usize)> {
    T::parse_partial(bytes)
}

pub trait Float: float::RawFloat {
    #[inline]
    fn parse(bytes: &[u8]) -> Option<Self> {
        match Self::parse_partial(bytes) {
            Some((v, n)) if n == bytes.len() => Some(v),
            _ => None,
        }
    }

    #[inline]
    fn parse_partial(bytes: &[u8]) -> Option<(Self, usize)> {
        dec2flt(bytes)
    }
}

impl Float for f32 {}
impl Float for f64 {}

/// Converts a `BiasedFp` to the closest machine float type.
fn biased_fp_to_float<T: RawFloat>(x: BiasedFp) -> T {
    let mut word = x.f;
    word |= (x.e as u64) << T::MANTISSA_EXPLICIT_BITS;
    T::from_u64_bits(word)
}

/// Converts a decimal string into a floating point number.
#[inline]
pub(crate) fn dec2flt<F: RawFloat>(mut s: &[u8]) -> Option<(F, usize)> {
    let start = s;
    let c = if let Some(&c) = s.first() {
        c
    } else {
        return None;
    };
    let negative = c == b'-';
    if negative || c == b'+' {
        s = &s[1..];
        if s.is_empty() {
            return None;
        }
    }

    let (mut num, len) = match parse_partial_number(s, start) {
        Some(r) => r,
        None => match parse_inf_nan(s, negative) {
            Some((value, len)) => return Some((value, len + s.offset_from(start) as usize)),
            None => return None,
        },
    };
    num.negative = negative;
    if let Some(value) = num.try_fast_path::<F>() {
        return Some((value, len));
    }

    // If significant digits were truncated, then we can have rounding error
    // only if `mantissa + 1` produces a different result. We also avoid
    // redundantly using the Eisel-Lemire algorithm if it was unable to
    // correctly round on the first pass.
    let mut fp = compute_float::<F>(num.exponent, num.mantissa);
    if num.many_digits && fp.e >= 0 && fp != compute_float::<F>(num.exponent, num.mantissa + 1) {
        fp.e = -1;
    }
    // Unable to correctly round the float using the Eisel-Lemire algorithm.
    // Fallback to a slower, but always correct algorithm.
    if fp.e < 0 {
        fp = parse_long_mantissa::<F>(s);
    }

    let mut float = biased_fp_to_float::<F>(fp);
    if num.negative {
        float = -float;
    }
    Some((float, len))
}

#[cfg(test)]
#[path = "../tests/float.rs"]
mod tests;