Skip to main content

clt_database/numeric/
mod.rs

1use crate::{types::AsValueRef, Value, ValueRef};
2
3pub mod decimal;
4pub mod nonnan;
5
6use nonnan::NonNan;
7
8// TODO: Remove when https://github.com/rust-lang/libs-team/issues/230 is available
9trait SaturatingShl {
10    fn saturating_shl(self, rhs: u32) -> Self;
11}
12
13impl SaturatingShl for i64 {
14    fn saturating_shl(self, rhs: u32) -> Self {
15        if rhs >= Self::BITS {
16            0
17        } else {
18            self << rhs
19        }
20    }
21}
22
23// TODO: Remove when https://github.com/rust-lang/libs-team/issues/230 is available
24trait SaturatingShr {
25    fn saturating_shr(self, rhs: u32) -> Self;
26}
27
28impl SaturatingShr for i64 {
29    fn saturating_shr(self, rhs: u32) -> Self {
30        if rhs >= Self::BITS {
31            if self >= 0 {
32                0
33            } else {
34                -1
35            }
36        } else {
37            self >> rhs
38        }
39    }
40}
41
42#[derive(Debug, Clone, Copy)]
43#[cfg_attr(
44    clt_turso_feature = "serde",
45    derive(serde::Serialize, serde::Deserialize)
46)]
47pub enum Numeric {
48    Integer(i64),
49    Float(NonNan),
50}
51
52impl Numeric {
53    pub fn from_value<T: AsValueRef>(value: T) -> Option<Self> {
54        let value = value.as_value_ref();
55
56        match value {
57            ValueRef::Null => None,
58            ValueRef::Numeric(v) => Some(v),
59            ValueRef::Text(text) => Some(Numeric::from(text.as_str())),
60            ValueRef::Blob(blob) => {
61                let text = String::from_utf8_lossy(blob);
62                Some(Numeric::from(&text))
63            }
64        }
65    }
66
67    #[inline]
68    pub fn from_value_strict(value: &Value) -> Option<Self> {
69        match value {
70            Value::Null | Value::Blob(_) => None,
71            Value::Numeric(n) => Some(*n),
72            Value::Text(text) => {
73                let s = text.as_str();
74
75                match str_to_f64(s) {
76                    None
77                    | Some(StrToF64::FractionalPrefix(_))
78                    | Some(StrToF64::DecimalPrefix(_)) => None,
79                    Some(StrToF64::Fractional(value)) => Some(Self::Float(value)),
80                    Some(StrToF64::Decimal(real)) => Some(match str_to_i64_checked(s) {
81                        Ok(integer) => Self::Integer(integer),
82                        Err(_) => Self::Float(real),
83                    }),
84                }
85            }
86        }
87    }
88
89    #[inline]
90    pub fn to_f64(&self) -> f64 {
91        match self {
92            Numeric::Integer(v) => *v as _,
93            Numeric::Float(v) => (*v).into(),
94        }
95    }
96
97    #[inline]
98    pub fn to_bool(&self) -> bool {
99        match self {
100            Numeric::Integer(0) => false,
101            Numeric::Float(non_nan) if *non_nan == 0.0 => false,
102            _ => true,
103        }
104    }
105
106    #[inline]
107    pub fn checked_add(self, rhs: Self) -> Option<Self> {
108        match (self, rhs) {
109            (Numeric::Integer(lhs), Numeric::Integer(rhs)) => match lhs.checked_add(rhs) {
110                None => Numeric::Float(lhs.into()).checked_add(Numeric::Float(rhs.into())),
111                Some(i) => Some(Numeric::Integer(i)),
112            },
113            (Numeric::Float(lhs), Numeric::Float(rhs)) => (lhs + rhs).map(Numeric::Float),
114            (f @ Numeric::Float(_), Numeric::Integer(i))
115            | (Numeric::Integer(i), f @ Numeric::Float(_)) => {
116                f.checked_add(Numeric::Float(i.into()))
117            }
118        }
119    }
120
121    #[inline]
122    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
123        match (self, rhs) {
124            (Numeric::Float(lhs), Numeric::Float(rhs)) => (lhs - rhs).map(Numeric::Float),
125            (Numeric::Integer(lhs), Numeric::Integer(rhs)) => match lhs.checked_sub(rhs) {
126                None => Numeric::Float(lhs.into()).checked_sub(Numeric::Float(rhs.into())),
127                Some(i) => Some(Numeric::Integer(i)),
128            },
129            (f @ Numeric::Float(_), Numeric::Integer(i)) => f.checked_sub(Numeric::Float(i.into())),
130            (Numeric::Integer(i), f @ Numeric::Float(_)) => Numeric::Float(i.into()).checked_sub(f),
131        }
132    }
133
134    #[inline]
135    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
136        match (self, rhs) {
137            (Numeric::Float(lhs), Numeric::Float(rhs)) => (lhs * rhs).map(Numeric::Float),
138            (Numeric::Integer(lhs), Numeric::Integer(rhs)) => match lhs.checked_mul(rhs) {
139                None => Numeric::Float(lhs.into()).checked_mul(Numeric::Float(rhs.into())),
140                Some(i) => Some(Numeric::Integer(i)),
141            },
142            (f @ Numeric::Float(_), Numeric::Integer(i))
143            | (Numeric::Integer(i), f @ Numeric::Float(_)) => {
144                f.checked_mul(Numeric::Float(i.into()))
145            }
146        }
147    }
148
149    #[inline]
150    pub fn checked_div(self, rhs: Self) -> Option<Self> {
151        match (self, rhs) {
152            (Numeric::Float(lhs), Numeric::Float(rhs)) => match lhs / rhs {
153                Some(v) if rhs != 0.0 => Some(Numeric::Float(v)),
154                _ => None,
155            },
156            (Numeric::Integer(lhs), Numeric::Integer(rhs)) => match lhs.checked_div(rhs) {
157                None => Numeric::Float(lhs.into()).checked_div(Numeric::Float(rhs.into())),
158                Some(v) => Some(Numeric::Integer(v)),
159            },
160            (f @ Numeric::Float(_), Numeric::Integer(i)) => f.checked_div(Numeric::Float(i.into())),
161            (Numeric::Integer(i), f @ Numeric::Float(_)) => Numeric::Float(i.into()).checked_div(f),
162        }
163    }
164}
165
166impl From<Numeric> for NullableInteger {
167    #[inline]
168    fn from(value: Numeric) -> Self {
169        match value {
170            Numeric::Integer(v) => NullableInteger::Integer(v),
171            Numeric::Float(v) => NullableInteger::Integer(f64::from(v) as i64),
172        }
173    }
174}
175
176impl From<Numeric> for Value {
177    #[inline]
178    fn from(value: Numeric) -> Self {
179        Value::Numeric(value)
180    }
181}
182
183impl From<Option<Numeric>> for Value {
184    fn from(value: Option<Numeric>) -> Self {
185        value.map_or_else(|| Value::Null, Value::from)
186    }
187}
188
189impl<T: AsRef<str>> From<T> for Numeric {
190    fn from(value: T) -> Self {
191        let text = value.as_ref();
192
193        match str_to_f64(text) {
194            None => Self::Integer(0),
195            Some(StrToF64::Fractional(value) | StrToF64::FractionalPrefix(value)) => {
196                Self::Float(value)
197            }
198            Some(StrToF64::Decimal(real) | StrToF64::DecimalPrefix(real)) => {
199                match str_to_i64_checked(text) {
200                    Ok(integer) => Self::Integer(integer),
201                    Err(_) => Self::Float(real),
202                }
203            }
204        }
205    }
206}
207
208impl From<Value> for Option<Numeric> {
209    fn from(value: Value) -> Self {
210        Self::from(&value)
211    }
212}
213impl From<&Value> for Option<Numeric> {
214    fn from(value: &Value) -> Self {
215        match value {
216            Value::Null => None,
217            Value::Numeric(n) => Some(*n),
218            Value::Text(text) => Some(Numeric::from(text.as_str())),
219            Value::Blob(blob) => {
220                let text = String::from_utf8_lossy(blob.as_slice());
221                Some(Numeric::from(&text))
222            }
223        }
224    }
225}
226
227impl std::ops::Neg for Numeric {
228    type Output = Self;
229
230    fn neg(self) -> Self::Output {
231        match self {
232            Numeric::Integer(v) => match v.checked_neg() {
233                None => -Numeric::Float(v.into()),
234                Some(i) => Numeric::Integer(i),
235            },
236            Numeric::Float(v) => Numeric::Float(-v),
237        }
238    }
239}
240
241impl PartialEq for Numeric {
242    fn eq(&self, other: &Self) -> bool {
243        self.cmp(other).is_eq()
244    }
245}
246
247impl Eq for Numeric {}
248
249impl PartialOrd for Numeric {
250    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
251        Some(self.cmp(other))
252    }
253}
254
255impl Ord for Numeric {
256    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
257        match (self, other) {
258            (Numeric::Integer(a), Numeric::Integer(b)) => a.cmp(b),
259            (Numeric::Float(a), Numeric::Float(b)) => {
260                let fa: f64 = (*a).into();
261                let fb: f64 = (*b).into();
262                // NonNan guarantees no NaN, so partial_cmp always returns Some.
263                // SQLite's float-vs-float uses raw IEEE 754 < and > operators,
264                // which both return false for NaN, resulting in "equal".
265                fa.partial_cmp(&fb).unwrap_or(std::cmp::Ordering::Equal)
266            }
267            (Numeric::Integer(int), Numeric::Float(float)) => {
268                sqlite_int_float_cmp(*int, f64::from(*float))
269            }
270            (Numeric::Float(float), Numeric::Integer(int)) => {
271                sqlite_int_float_cmp(*int, f64::from(*float)).reverse()
272            }
273        }
274    }
275}
276
277/// Compare an integer and a float following SQLite semantics.
278///
279/// SQLite treats NaN as NULL, so int > NaN. In practice, NonNan prevents NaN
280/// from appearing in Numeric::Float, but we check defensively.
281///
282/// See sqlite3IntFloatCompare in src/vdbeaux.c.
283fn sqlite_int_float_cmp(int_val: i64, float_val: f64) -> std::cmp::Ordering {
284    if float_val.is_nan() {
285        // NaN is treated as NULL; all integers are greater than NULL
286        return std::cmp::Ordering::Greater;
287    }
288
289    if float_val < -9_223_372_036_854_775_808.0 {
290        return std::cmp::Ordering::Greater;
291    }
292    if float_val >= 9_223_372_036_854_775_808.0 {
293        return std::cmp::Ordering::Less;
294    }
295
296    let float_as_int = float_val as i64;
297    match int_val.cmp(&float_as_int) {
298        std::cmp::Ordering::Equal => {
299            let int_as_float = int_val as f64;
300            int_as_float
301                .partial_cmp(&float_val)
302                .unwrap_or(std::cmp::Ordering::Equal)
303        }
304        other => other,
305    }
306}
307
308#[derive(Debug)]
309pub enum NullableInteger {
310    Null,
311    Integer(i64),
312}
313
314impl From<NullableInteger> for Value {
315    fn from(value: NullableInteger) -> Self {
316        match value {
317            NullableInteger::Null => Value::Null,
318            NullableInteger::Integer(v) => Value::from_i64(v),
319        }
320    }
321}
322
323impl<T: AsRef<str>> From<T> for NullableInteger {
324    fn from(value: T) -> Self {
325        Self::Integer(str_to_i64(value.as_ref()).unwrap_or(0))
326    }
327}
328
329impl From<Value> for NullableInteger {
330    fn from(value: Value) -> Self {
331        Self::from(&value)
332    }
333}
334
335impl From<&Value> for NullableInteger {
336    fn from(value: &Value) -> Self {
337        match value {
338            Value::Null => Self::Null,
339            Value::Numeric(Numeric::Integer(v)) => Self::Integer(*v),
340            Value::Numeric(Numeric::Float(v)) => Self::Integer(f64::from(*v) as i64),
341            Value::Text(text) => Self::from(text.as_str()),
342            Value::Blob(blob) => {
343                let text = String::from_utf8_lossy(blob.as_slice());
344                Self::from(text)
345            }
346        }
347    }
348}
349
350impl std::ops::Not for NullableInteger {
351    type Output = Self;
352
353    fn not(self) -> Self::Output {
354        match self {
355            NullableInteger::Null => NullableInteger::Null,
356            NullableInteger::Integer(lhs) => NullableInteger::Integer(!lhs),
357        }
358    }
359}
360
361impl std::ops::BitAnd for NullableInteger {
362    type Output = Self;
363
364    fn bitand(self, rhs: Self) -> Self::Output {
365        match (self, rhs) {
366            (NullableInteger::Null, _) | (_, NullableInteger::Null) => NullableInteger::Null,
367            (NullableInteger::Integer(lhs), NullableInteger::Integer(rhs)) => {
368                NullableInteger::Integer(lhs & rhs)
369            }
370        }
371    }
372}
373
374impl std::ops::BitOr for NullableInteger {
375    type Output = Self;
376
377    fn bitor(self, rhs: Self) -> Self::Output {
378        match (self, rhs) {
379            (NullableInteger::Null, _) | (_, NullableInteger::Null) => NullableInteger::Null,
380            (NullableInteger::Integer(lhs), NullableInteger::Integer(rhs)) => {
381                NullableInteger::Integer(lhs | rhs)
382            }
383        }
384    }
385}
386
387impl std::ops::Shl for NullableInteger {
388    type Output = Self;
389
390    fn shl(self, rhs: Self) -> Self::Output {
391        match (self, rhs) {
392            (NullableInteger::Null, _) | (_, NullableInteger::Null) => NullableInteger::Null,
393            (NullableInteger::Integer(lhs), NullableInteger::Integer(rhs)) => {
394                NullableInteger::Integer(if rhs.is_positive() {
395                    lhs.saturating_shl(rhs.try_into().unwrap_or(u32::MAX))
396                } else {
397                    lhs.saturating_shr(rhs.saturating_abs().try_into().unwrap_or(u32::MAX))
398                })
399            }
400        }
401    }
402}
403
404impl std::ops::Shr for NullableInteger {
405    type Output = Self;
406
407    fn shr(self, rhs: Self) -> Self::Output {
408        match (self, rhs) {
409            (NullableInteger::Null, _) | (_, NullableInteger::Null) => NullableInteger::Null,
410            (NullableInteger::Integer(lhs), NullableInteger::Integer(rhs)) => {
411                NullableInteger::Integer(if rhs.is_positive() {
412                    lhs.saturating_shr(rhs.try_into().unwrap_or(u32::MAX))
413                } else {
414                    lhs.saturating_shl(rhs.saturating_abs().try_into().unwrap_or(u32::MAX))
415                })
416            }
417        }
418    }
419}
420
421impl std::ops::Rem for NullableInteger {
422    type Output = Self;
423
424    fn rem(self, rhs: Self) -> Self::Output {
425        match (self, rhs) {
426            (NullableInteger::Null, _) | (_, NullableInteger::Null) => NullableInteger::Null,
427            (_, NullableInteger::Integer(0)) => NullableInteger::Null,
428            (lhs, NullableInteger::Integer(-1)) => lhs % NullableInteger::Integer(1),
429            (NullableInteger::Integer(lhs), NullableInteger::Integer(rhs)) => {
430                NullableInteger::Integer(lhs % rhs)
431            }
432        }
433    }
434}
435
436// Maximum u64 that can survive a f64 round trip
437const MAX_EXACT: u64 = u64::MAX << 11;
438
439const VERTICAL_TAB: char = '\u{b}';
440
441/// Encapsulates Dekker's arithmetic for higher precision. This is spiritually the same as using a
442/// f128 for arithmetic, but cross platform and compatible with sqlite.
443#[derive(Debug, Clone, Copy)]
444pub struct DoubleDouble(pub f64, pub f64);
445
446impl DoubleDouble {
447    pub const E100: Self = DoubleDouble(1.0e+100, -1.590_289_110_975_991_8e83);
448    pub const E10: Self = DoubleDouble(1.0e+10, 0.0);
449    pub const E1: Self = DoubleDouble(1.0e+01, 0.0);
450
451    pub const NEG_E100: Self = DoubleDouble(1.0e-100, -1.999_189_980_260_288_3e-117);
452    pub const NEG_E10: Self = DoubleDouble(1.0e-10, -3.643_219_731_549_774e-27);
453    pub const NEG_E1: Self = DoubleDouble(1.0e-01, -5.551_115_123_125_783e-18);
454}
455
456impl From<u64> for DoubleDouble {
457    fn from(value: u64) -> Self {
458        let r = value as f64;
459
460        // If the value is smaller than MAX_EXACT, the error isn't significant
461        let rr = if r <= MAX_EXACT as f64 {
462            let round_tripped = value as f64 as u64;
463            let sign = if value >= round_tripped { 1.0 } else { -1.0 };
464
465            // Error term is the signed distance of the round tripped value and itself
466            sign * value.abs_diff(round_tripped) as f64
467        } else {
468            0.0
469        };
470
471        DoubleDouble(r, rr)
472    }
473}
474
475impl From<DoubleDouble> for u64 {
476    fn from(value: DoubleDouble) -> Self {
477        if value.1 < 0.0 {
478            value.0 as u64 - value.1.abs() as u64
479        } else {
480            value.0 as u64 + value.1 as u64
481        }
482    }
483}
484
485impl From<DoubleDouble> for f64 {
486    fn from(DoubleDouble(a, aa): DoubleDouble) -> Self {
487        a + aa
488    }
489}
490
491impl std::ops::Mul for DoubleDouble {
492    type Output = Self;
493
494    /// Double-Double multiplication.  (self.0, self.1) *= (rhs.0, rhs.1)
495    ///
496    /// Reference:
497    ///   T. J. Dekker, "A Floating-Point Technique for Extending the Available Precision".
498    ///   1971-07-26.
499    ///
500    fn mul(self, rhs: Self) -> Self::Output {
501        // TODO: Better variable naming
502
503        let mask = u64::MAX << 26;
504
505        let hx = f64::from_bits(self.0.to_bits() & mask);
506        let tx = self.0 - hx;
507
508        let hy = f64::from_bits(rhs.0.to_bits() & mask);
509        let ty = rhs.0 - hy;
510
511        let p = hx * hy;
512        let q = hx * ty + tx * hy;
513
514        let c = p + q;
515        let cc = p - c + q + tx * ty;
516        let cc = self.0 * rhs.1 + self.1 * rhs.0 + cc;
517
518        let r = c + cc;
519        let rr = (c - r) + cc;
520
521        DoubleDouble(r, rr)
522    }
523}
524
525impl std::ops::MulAssign for DoubleDouble {
526    fn mul_assign(&mut self, rhs: Self) {
527        *self = *self * rhs;
528    }
529}
530
531pub fn str_to_i64(input: impl AsRef<str>) -> Option<i64> {
532    Some(match str_to_i64_checked(input) {
533        Ok(v) => v,
534        Err(IntOverflow::Positive) => i64::MAX,
535        Err(IntOverflow::Negative) => i64::MIN,
536    })
537}
538
539#[derive(Debug, Clone, Copy)]
540pub enum IntOverflow {
541    Positive,
542    Negative,
543}
544
545/// Like [`str_to_i64`] but distinguishes overflow from a successful parse.
546/// Returns `Err(_)` when the leading numeric prefix does not fit in i64.
547pub fn str_to_i64_checked(input: impl AsRef<str>) -> Result<i64, IntOverflow> {
548    let input = input
549        .as_ref()
550        .trim_matches(|ch: char| ch.is_ascii_whitespace() || ch == VERTICAL_TAB);
551
552    let mut iter = input.chars().enumerate().peekable();
553
554    iter.next_if(|(_, ch)| matches!(ch, '+' | '-'));
555    let Some((end, _)) = iter.take_while(|(_, ch)| ch.is_ascii_digit()).last() else {
556        return Ok(0);
557    };
558
559    input[0..=end]
560        .parse::<i64>()
561        .map_err(|err| match err.kind() {
562            std::num::IntErrorKind::PosOverflow => IntOverflow::Positive,
563            std::num::IntErrorKind::NegOverflow => IntOverflow::Negative,
564            // Empty/InvalidDigit are ruled out: the early `return Ok(0)` above
565            // fires when there are no digits, and the slice spans only an
566            // optional sign followed by ASCII digits. Zero is produced only by
567            // `NonZeroI*::from_str`, not by `i64::from_str`. `IntErrorKind` is
568            // `#[non_exhaustive]`, hence the catch-all.
569            _ => unreachable!("unexpected IntErrorKind from i64::from_str: {err:?}"),
570        })
571}
572
573#[derive(Debug, Clone, Copy)]
574pub enum StrToF64 {
575    Fractional(NonNan),
576    Decimal(NonNan),
577    FractionalPrefix(NonNan),
578    DecimalPrefix(NonNan),
579}
580
581impl From<StrToF64> for f64 {
582    fn from(value: StrToF64) -> Self {
583        match value {
584            StrToF64::Fractional(non_nan) => non_nan.into(),
585            StrToF64::Decimal(non_nan) => non_nan.into(),
586            StrToF64::FractionalPrefix(non_nan) => non_nan.into(),
587            StrToF64::DecimalPrefix(non_nan) => non_nan.into(),
588        }
589    }
590}
591
592pub fn str_to_f64(input: impl AsRef<str>) -> Option<StrToF64> {
593    let mut input = input
594        .as_ref()
595        .trim_matches(|ch: char| ch.is_ascii_whitespace() || ch == VERTICAL_TAB)
596        .chars()
597        .peekable();
598
599    let sign = match input.next_if(|ch| matches!(ch, '-' | '+')) {
600        Some('-') => -1.0,
601        _ => 1.0,
602    };
603
604    let mut had_digits = false;
605    let mut is_fractional = false;
606
607    let mut significant: u64 = 0;
608
609    // Copy as many significant digits as we can
610    while let Some(digit) = input.peek().and_then(|ch| ch.to_digit(10)) {
611        had_digits = true;
612
613        match significant
614            .checked_mul(10)
615            .and_then(|v| v.checked_add(digit as u64))
616        {
617            Some(new) => significant = new,
618            None => break,
619        }
620
621        input.next();
622    }
623
624    let mut exponent = 0;
625
626    // Increment the exponent for every non significant digit we skipped
627    while input.next_if(char::is_ascii_digit).is_some() {
628        exponent += 1
629    }
630
631    if input.next_if(|ch| matches!(ch, '.')).is_some() {
632        if had_digits {
633            is_fractional = true;
634        }
635
636        if input.peek().is_some_and(char::is_ascii_digit) {
637            is_fractional = true;
638        }
639
640        while let Some(digit) = input.peek().and_then(|ch| ch.to_digit(10)) {
641            if significant < (u64::MAX - 9) / 10 {
642                significant = significant * 10 + digit as u64;
643                exponent -= 1;
644            }
645
646            input.next();
647        }
648    };
649
650    let mut valid_exponent = true;
651
652    if (had_digits || is_fractional) && input.next_if(|ch| matches!(ch, 'e' | 'E')).is_some() {
653        let sign = match input.next_if(|ch| matches!(ch, '-' | '+')) {
654            Some('-') => -1,
655            _ => 1,
656        };
657
658        if input.peek().is_some_and(char::is_ascii_digit) {
659            is_fractional = true;
660            let mut e = 0;
661
662            while let Some(ch) = input.next_if(char::is_ascii_digit) {
663                e = (e * 10 + ch.to_digit(10).unwrap() as i32).min(1000);
664            }
665
666            exponent += sign * e;
667        } else {
668            valid_exponent = false;
669        }
670    };
671
672    if !(had_digits || is_fractional) {
673        return None;
674    }
675
676    while exponent.is_positive() && significant < MAX_EXACT / 10 {
677        significant *= 10;
678        exponent -= 1;
679    }
680
681    while exponent.is_negative() && significant % 10 == 0 {
682        significant /= 10;
683        exponent += 1;
684    }
685
686    let mut result = DoubleDouble::from(significant);
687
688    if exponent > 0 {
689        while exponent >= 100 {
690            exponent -= 100;
691            result *= DoubleDouble::E100;
692        }
693        while exponent >= 10 {
694            exponent -= 10;
695            result *= DoubleDouble::E10;
696        }
697        while exponent >= 1 {
698            exponent -= 1;
699            result *= DoubleDouble::E1;
700        }
701    } else {
702        while exponent <= -100 {
703            exponent += 100;
704            result *= DoubleDouble::NEG_E100;
705        }
706        while exponent <= -10 {
707            exponent += 10;
708            result *= DoubleDouble::NEG_E10;
709        }
710        while exponent <= -1 {
711            exponent += 1;
712            result *= DoubleDouble::NEG_E1;
713        }
714    }
715
716    let result = NonNan::new(f64::from(result) * sign)
717        .unwrap_or_else(|| NonNan::new(sign * f64::INFINITY).unwrap());
718
719    if !valid_exponent || input.count() > 0 {
720        if is_fractional {
721            return Some(StrToF64::FractionalPrefix(result));
722        } else {
723            return Some(StrToF64::DecimalPrefix(result));
724        }
725    }
726
727    Some(if is_fractional {
728        StrToF64::Fractional(result)
729    } else {
730        StrToF64::Decimal(result)
731    })
732}
733
734enum FloatParts {
735    Special(String),
736    Normal {
737        negative: bool,
738        digits: Vec<u8>,
739        exp: i32,
740    },
741}
742
743fn decompose_float(v: f64, precision: usize) -> FloatParts {
744    if v.is_nan() {
745        return FloatParts::Special("".to_string());
746    }
747
748    if v.is_infinite() {
749        return FloatParts::Special(if v.is_sign_negative() { "-Inf" } else { "Inf" }.to_string());
750    }
751
752    if v == 0.0 {
753        return FloatParts::Special("0.0".to_string());
754    }
755
756    let negative = v < 0.0;
757    let mut d = DoubleDouble(v.abs(), 0.0);
758    let mut exp = 0;
759
760    if d.0 > 9.223_372_036_854_775e18 {
761        while d.0 > 9.223_372_036_854_774e118 {
762            exp += 100;
763            d *= DoubleDouble::NEG_E100;
764        }
765        while d.0 > 9.223_372_036_854_774e28 {
766            exp += 10;
767            d *= DoubleDouble::NEG_E10;
768        }
769        while d.0 > 9.223_372_036_854_775e18 {
770            exp += 1;
771            d *= DoubleDouble::NEG_E1;
772        }
773    } else {
774        while d.0 < 9.223_372_036_854_775e-83 {
775            exp -= 100;
776            d *= DoubleDouble::E100;
777        }
778        while d.0 < 9.223_372_036_854_775e7 {
779            exp -= 10;
780            d *= DoubleDouble::E10;
781        }
782        while d.0 < 9.223_372_036_854_775e17 {
783            exp -= 1;
784            d *= DoubleDouble::E1;
785        }
786    }
787
788    let mut digits = u64::from(d).to_string().into_bytes();
789    let mut decimal_pos = digits.len() as i32 + exp;
790
791    'out: {
792        if digits.len() > precision {
793            let round_up = digits[precision] >= b'5';
794            digits.truncate(precision);
795
796            if round_up {
797                for i in (0..precision).rev() {
798                    if digits[i] < b'9' {
799                        digits[i] += 1;
800                        break 'out;
801                    }
802                    digits[i] = b'0';
803                }
804
805                digits.insert(0, b'1');
806                decimal_pos += 1;
807            }
808        }
809    }
810
811    while digits.len() > 1 && digits[digits.len() - 1] == b'0' {
812        digits.pop();
813    }
814
815    FloatParts::Normal {
816        negative,
817        digits,
818        exp: decimal_pos - 1,
819    }
820}
821
822fn format_float_scientific(v: f64, precision: usize) -> String {
823    match decompose_float(v, precision) {
824        FloatParts::Special(s) => s,
825        FloatParts::Normal {
826            negative,
827            digits,
828            exp,
829        } => {
830            let first = digits.first().cloned().unwrap_or(b'0') as char;
831            let rest = digits
832                .get(1..)
833                .filter(|v| !v.is_empty())
834                .map(|v| unsafe { str::from_utf8_unchecked(v) })
835                .unwrap_or("0");
836            format!(
837                "{}{}.{}e{}{:0width$}",
838                if negative { "-" } else { "" },
839                first,
840                rest,
841                if exp.is_positive() { "+" } else { "-" },
842                exp.abs(),
843                width = if exp.abs() > 99 { 3 } else { 2 }
844            )
845        }
846    }
847}
848
849pub fn format_float(v: f64) -> String {
850    match decompose_float(v, 15) {
851        FloatParts::Special(s) => s,
852        FloatParts::Normal {
853            negative,
854            digits,
855            exp,
856        } => {
857            let decimal_pos = exp + 1;
858            if (-4..=14).contains(&exp) {
859                format!(
860                    "{}{}.{}{}",
861                    if negative { "-" } else { Default::default() },
862                    if decimal_pos > 0 {
863                        let zeroes = (decimal_pos - digits.len() as i32).max(0) as usize;
864                        let digits = digits
865                            .get(0..(decimal_pos.min(digits.len() as i32) as usize))
866                            .unwrap();
867                        (unsafe { str::from_utf8_unchecked(digits) }).to_owned()
868                            + &"0".repeat(zeroes)
869                    } else {
870                        "0".to_string()
871                    },
872                    "0".repeat(decimal_pos.min(0).unsigned_abs() as usize),
873                    digits
874                        .get((decimal_pos.max(0) as usize)..)
875                        .filter(|v| !v.is_empty())
876                        .map(|v| unsafe { str::from_utf8_unchecked(v) })
877                        .unwrap_or("0")
878                )
879            } else {
880                format_float_scientific(v, 15)
881            }
882        }
883    }
884}
885
886pub fn format_float_for_quote(v: f64) -> String {
887    let default = format_float(v);
888    if str_to_f64(&default).map(f64::from) == Some(v) {
889        return default;
890    }
891    format_float_scientific(v, 19)
892}
893
894#[test]
895fn test_decode_float() {
896    assert_eq!(format_float(9.93e-322), "9.93071948140905e-322");
897    assert_eq!(format_float(9.93), "9.93");
898    assert_eq!(format_float(0.093), "0.093");
899    assert_eq!(format_float(-0.093), "-0.093");
900    assert_eq!(format_float(0.0), "0.0");
901    assert_eq!(format_float(4.94e-322), "4.94065645841247e-322");
902    assert_eq!(format_float(-20228007.0), "-20228007.0");
903}