1use crate::{types::AsValueRef, Value, ValueRef};
2
3pub mod decimal;
4pub mod nonnan;
5
6use nonnan::NonNan;
7
8trait 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
23trait 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 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
274fn sqlite_int_float_cmp(int_val: i64, float_val: f64) -> std::cmp::Ordering {
281 if float_val.is_nan() {
282 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
433const MAX_EXACT: u64 = u64::MAX << 11;
435
436const VERTICAL_TAB: char = '\u{b}';
437
438#[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 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 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 fn mul(self, rhs: Self) -> Self::Output {
498 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
542pub 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 _ => 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 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 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}