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(
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 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
277fn sqlite_int_float_cmp(int_val: i64, float_val: f64) -> std::cmp::Ordering {
284 if float_val.is_nan() {
285 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
436const MAX_EXACT: u64 = u64::MAX << 11;
438
439const VERTICAL_TAB: char = '\u{b}';
440
441#[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 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 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 fn mul(self, rhs: Self) -> Self::Output {
501 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
545pub 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 _ => 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 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 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}