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