1use std::fmt::Write as _;
10
11use rupg_common::SqlState;
12
13use crate::error::TypeError;
14use crate::number::is_space;
15
16struct Stack {
18 buf: [u8; 64],
19 len: usize,
20}
21
22impl Stack {
23 fn new() -> Stack {
24 Stack { buf: [0; 64], len: 0 }
25 }
26
27 fn bytes(&self) -> &[u8] {
28 &self.buf[..self.len]
29 }
30}
31
32impl std::fmt::Write for Stack {
33 fn write_str(&mut self, s: &str) -> std::fmt::Result {
34 let end = self.len + s.len();
35 self.buf.get_mut(self.len..end).ok_or(std::fmt::Error)?.copy_from_slice(s.as_bytes());
36 self.len = end;
37 Ok(())
38 }
39}
40
41fn split_exp(text: &[u8]) -> (&[u8], i32) {
43 let e = text.iter().position(|&b| b == b'e').unwrap_or(text.len());
44 let exp = std::str::from_utf8(&text[e + 1..]).ok().and_then(|t| t.parse().ok()).unwrap_or(0);
45 (&text[..e], exp)
46}
47
48fn exponent(exp: i32, out: &mut Vec<u8>) {
50 out.push(b'e');
51 out.push(if exp < 0 { b'-' } else { b'+' });
52 let exp = exp.unsigned_abs();
53 if exp < 10 {
54 out.push(b'0');
55 }
56 crate::number::u64_out(u64::from(exp), out);
57}
58
59fn fixed(digits: &[u8], exp: i32, out: &mut Vec<u8>) {
61 let n = digits.len() as i32;
62 if exp < 0 {
63 out.extend_from_slice(b"0.");
64 out.extend(std::iter::repeat_n(b'0', (-exp - 1) as usize));
65 out.extend_from_slice(digits);
66 } else if exp + 1 >= n {
67 out.extend_from_slice(digits);
68 out.extend(std::iter::repeat_n(b'0', (exp + 1 - n) as usize));
69 } else {
70 let point = (exp + 1) as usize;
71 out.extend_from_slice(&digits[..point]);
72 out.push(b'.');
73 out.extend_from_slice(&digits[point..]);
74 }
75}
76
77fn plain(mantissa: &[u8]) -> Vec<u8> {
79 mantissa.iter().copied().filter(|&b| b != b'.').collect()
80}
81
82fn shortest(sci: &[u8], fixed_limit: i32, out: &mut Vec<u8>) {
84 let (digits, exp) = split_exp(sci);
85 if (-4..fixed_limit).contains(&exp) {
86 fixed(&plain(digits), exp, out);
87 } else {
88 out.extend_from_slice(digits);
90 exponent(exp, out);
91 }
92}
93
94trait Shortest: Copy + PartialEq + std::fmt::LowerExp + std::str::FromStr {
96 const DIGITS: usize;
98 fn parts(self) -> (u64, i32, bool);
100}
101
102impl Shortest for f64 {
103 const DIGITS: usize = 17;
104 fn parts(self) -> (u64, i32, bool) {
105 let bits = self.to_bits();
106 let (exp, frac) = ((bits >> 52) & 0x7ff, bits & ((1 << 52) - 1));
107 match exp {
108 0 => (frac, -1074, false),
109 _ => (frac | 1 << 52, exp as i32 - 1075, frac == 0 && exp > 1),
110 }
111 }
112}
113
114impl Shortest for f32 {
115 const DIGITS: usize = 9;
116 fn parts(self) -> (u64, i32, bool) {
117 let bits = self.to_bits();
118 let (exp, frac) = ((bits >> 23) & 0xff, bits & ((1 << 23) - 1));
119 match exp {
120 0 => (u64::from(frac), -149, false),
121 _ => (u64::from(frac | 1 << 23), exp as i32 - 150, frac == 0 && exp > 1),
122 }
123 }
124}
125
126fn same_value(d: u64, e10: i32, c: u64, e2: i32) -> bool {
128 if !(-24..=24).contains(&e10) {
129 return false;
130 }
131 let left = u128::from(d) * 5u128.pow(e10.max(0).unsigned_abs());
132 let right = u128::from(c) * 5u128.pow(e10.min(0).unsigned_abs());
133 let shift = e10 - e2;
135 let (small, large, shift) =
136 if shift >= 0 { (left, right, shift) } else { (right, left, -shift) };
137 shift < 128 && large.trailing_zeros() >= shift.unsigned_abs() && large >> shift == small
138}
139
140fn halfway<F: Shortest>(v: F, d: u64, e10: i32) -> bool {
142 let (m, e, nearer_below) = v.parts();
143 let below = if nearer_below { (4 * m - 1, e - 2) } else { (2 * m - 1, e - 1) };
144 same_value(d, e10, 2 * m + 1, e - 1) || same_value(d, e10, below.0, below.1)
145}
146
147fn decimal(sci: &[u8]) -> (u64, i32) {
149 let (digits, exp) = split_exp(sci);
150 let digits = plain(digits);
151 let d = digits.iter().fold(0u64, |d, &b| d * 10 + u64::from(b - b'0'));
152 (d, exp + 1 - digits.len() as i32)
153}
154
155fn sci_text(mut d: u64, mut e10: i32) -> Stack {
157 while d.is_multiple_of(10) && d != 0 {
158 d /= 10;
159 e10 += 1;
160 }
161 let mut digits = Stack::new();
162 let _ = write!(digits, "{d}");
163 let digits = digits.bytes();
164 let mut sci = Stack::new();
165 let _ = sci.write_str(std::str::from_utf8(&digits[..1]).unwrap_or("0"));
166 if digits.len() > 1 {
167 let _ = sci.write_str(".");
168 let _ = sci.write_str(std::str::from_utf8(&digits[1..]).unwrap_or(""));
169 }
170 let _ = write!(sci, "e{}", e10 + digits.len() as i32 - 1);
171 sci
172}
173
174fn inside<F: Shortest>(v: F, d: u64, e10: i32) -> bool {
176 let mut text = Stack::new();
177 let _ = write!(text, "{d}e{e10}");
178 let reads_back = std::str::from_utf8(text.bytes()).ok().and_then(|t| t.parse::<F>().ok());
179 reads_back == Some(v) && !halfway(v, d, e10)
180}
181
182fn shortest_sci<F: Shortest>(v: F) -> Stack {
184 let mut sci = Stack::new();
185 let _ = write!(sci, "{v:e}");
186 let (d, e10) = decimal(sci.bytes());
187 if !halfway(v, d, e10) {
188 return even_tie(v, d, e10).map_or(sci, |d| sci_text(d, e10));
189 }
190 let len = split_exp(sci.bytes()).0.iter().filter(|&&b| b != b'.').count();
191 for len in len + 1..=F::DIGITS {
192 let mut near = Stack::new();
193 let _ = write!(near, "{:.*e}", len - 1, v);
194 let (d, e10) = decimal(near.bytes());
195 for d in [d, d + 1, d - 1] {
197 if inside(v, d, e10) {
198 return sci_text(even_tie(v, d, e10).unwrap_or(d), e10);
199 }
200 }
201 }
202 sci
203}
204
205fn even_tie<F: Shortest>(v: F, d: u64, e10: i32) -> Option<u64> {
207 if d.is_multiple_of(2) {
208 return None;
209 }
210 let (m, e, _) = v.parts();
211 [(d - 1, 2 * d - 1), (d + 1, 2 * d + 1)]
212 .into_iter()
213 .find(|&(even, twice)| same_value(twice * 5, e10 - 1, m, e) && inside(v, even, e10))
214 .map(|(even, _)| even)
215}
216
217fn general(sci: &[u8], precision: i32, out: &mut Vec<u8>) {
219 let (digits, exp) = split_exp(sci);
220 let mut digits = plain(digits);
221 while digits.len() > 1 && digits.last() == Some(&b'0') {
222 digits.pop();
223 }
224 if exp < -4 || exp >= precision {
225 out.push(digits[0]);
226 if digits.len() > 1 {
227 out.push(b'.');
228 out.extend_from_slice(&digits[1..]);
229 }
230 exponent(exp, out);
231 } else {
232 fixed(&digits, exp, out);
233 }
234}
235
236fn special(nan: bool, inf: bool, zero: bool, negative: bool, out: &mut Vec<u8>) -> bool {
238 if nan {
239 out.extend_from_slice(b"NaN");
240 return true;
241 }
242 if negative {
243 out.push(b'-');
244 }
245 if inf {
246 out.extend_from_slice(b"Infinity");
247 } else if zero {
248 out.push(b'0');
249 }
250 inf || zero
251}
252
253pub fn float8_out(v: f64, extra_float_digits: i32, out: &mut Vec<u8>) {
255 if special(v.is_nan(), v.is_infinite(), v == 0.0, v.is_sign_negative(), out) {
256 return;
257 }
258 if extra_float_digits > 0 {
259 shortest(shortest_sci(v.abs()).bytes(), 15, out);
260 return;
261 }
262 let precision = (15 + extra_float_digits).clamp(1, 32);
263 let mut sci = Stack::new();
264 let _ = write!(sci, "{:.*e}", precision as usize - 1, v.abs());
265 general(sci.bytes(), precision, out);
266}
267
268pub fn float4_out(v: f32, extra_float_digits: i32, out: &mut Vec<u8>) {
270 if special(v.is_nan(), v.is_infinite(), v == 0.0, v.is_sign_negative(), out) {
271 return;
272 }
273 if extra_float_digits > 0 {
274 shortest(shortest_sci(v.abs()).bytes(), 6, out);
275 return;
276 }
277 let precision = (6 + extra_float_digits).clamp(1, 32);
279 let mut sci = Stack::new();
280 let _ = write!(sci, "{:.*e}", precision as usize - 1, f64::from(v.abs()));
281 general(sci.bytes(), precision, out);
282}
283
284pub(crate) trait Float: Copy + std::str::FromStr {
286 const BITS: u32;
288 const MIN_EXP: i64;
290 const MAX_EXP: i64;
292 const INFINITY: Self;
293 const NAN: Self;
294 fn from_parts(mantissa: u64, exp: i64) -> Self;
295 fn negate(self) -> Self;
296 fn is_zero(self) -> bool;
297 fn is_infinite(self) -> bool;
298}
299
300impl Float for f64 {
301 const BITS: u32 = 53;
302 const MIN_EXP: i64 = -1074;
303 const MAX_EXP: i64 = 1023;
304 const INFINITY: f64 = f64::INFINITY;
305 const NAN: f64 = f64::NAN;
306 fn from_parts(mantissa: u64, exp: i64) -> f64 {
307 if mantissa >> 52 == 0 {
308 return f64::from_bits(mantissa);
309 }
310 f64::from_bits(((exp + 52 + 1023) as u64) << 52 | (mantissa & ((1 << 52) - 1)))
311 }
312 fn negate(self) -> f64 {
313 -self
314 }
315 fn is_zero(self) -> bool {
316 self == 0.0
317 }
318 fn is_infinite(self) -> bool {
319 f64::is_infinite(self)
320 }
321}
322
323impl Float for f32 {
324 const BITS: u32 = 24;
325 const MIN_EXP: i64 = -149;
326 const MAX_EXP: i64 = 127;
327 const INFINITY: f32 = f32::INFINITY;
328 const NAN: f32 = f32::NAN;
329 fn from_parts(mantissa: u64, exp: i64) -> f32 {
330 if mantissa >> 23 == 0 {
331 return f32::from_bits(mantissa as u32);
332 }
333 f32::from_bits(((exp + 23 + 127) as u32) << 23 | (mantissa as u32 & ((1 << 23) - 1)))
334 }
335 fn negate(self) -> f32 {
336 -self
337 }
338 fn is_zero(self) -> bool {
339 self == 0.0
340 }
341 fn is_infinite(self) -> bool {
342 f32::is_infinite(self)
343 }
344}
345
346fn from_binary<F: Float>(mantissa: u64, sticky: bool, exp: i64) -> F {
348 if mantissa == 0 {
349 return F::from_parts(0, 0);
350 }
351 let bits = i64::from(64 - mantissa.leading_zeros());
352 let mut e = (exp + bits - i64::from(F::BITS)).max(F::MIN_EXP);
353 let shift = e - exp;
354 let mut m = if shift <= 0 {
355 mantissa << -shift
356 } else if shift > 64 {
357 0
358 } else {
359 let kept = if shift == 64 { 0 } else { mantissa >> shift };
360 let lost = if shift == 64 { mantissa } else { mantissa & ((1 << shift) - 1) };
361 let half = 1u64 << (shift - 1);
362 let up = lost > half || (lost == half && (sticky || kept & 1 == 1));
363 kept + u64::from(up)
364 };
365 if m == 1 << F::BITS {
366 m >>= 1;
367 e += 1;
368 }
369 if e + i64::from(F::BITS) - 1 > F::MAX_EXP {
370 return F::INFINITY;
371 }
372 F::from_parts(m, e)
373}
374
375pub(crate) fn strtod<F: Float>(b: &[u8]) -> Option<(F, usize, bool)> {
377 let at = |i: usize| b.get(i).copied().unwrap_or(0);
378 let starts = |i: usize, word: &[u8]| {
379 b.get(i..i + word.len()).is_some_and(|s| s.eq_ignore_ascii_case(word))
380 };
381 let neg = at(0) == b'-';
382 let mut i = usize::from(neg || at(0) == b'+');
383 let sign = |v: F| if neg { v.negate() } else { v };
384
385 if starts(i, b"inf") {
386 let end = if starts(i, b"infinity") { i + 8 } else { i + 3 };
387 return Some((sign(F::INFINITY), end, false));
388 }
389 if starts(i, b"nan") {
390 let mut end = i + 3;
391 if at(end) == b'(' {
392 let mut j = end + 1;
393 while at(j).is_ascii_alphanumeric() || at(j) == b'_' {
394 j += 1;
395 }
396 if at(j) == b')' {
397 end = j + 1;
398 }
399 }
400 return Some((sign(F::NAN), end, false));
401 }
402
403 if at(i) == b'0' && matches!(at(i + 1), b'x' | b'X') {
404 let start = i + 2;
405 let mut j = start;
406 let (mut m, mut sticky, mut exp, mut taken, mut any) = (0u64, false, 0i64, 0, false);
407 let mut point = false;
408 loop {
409 let c = at(j);
410 if c == b'.' && !point {
411 point = true;
412 } else if let Some(d) = (c as char).to_digit(16) {
413 any = true;
414 if m == 0 && d == 0 {
415 exp -= if point { 4 } else { 0 };
417 } else if taken < 16 {
418 m = m << 4 | u64::from(d);
419 taken += 1;
420 exp -= if point { 4 } else { 0 };
421 } else {
422 sticky |= d != 0;
423 exp += if point { 0 } else { 4 };
424 }
425 } else {
426 break;
427 }
428 j += 1;
429 }
430 if any {
431 if matches!(at(j), b'p' | b'P') {
432 let mut k = j + 1;
433 let negative = at(k) == b'-';
434 k += usize::from(negative || at(k) == b'+');
435 if at(k).is_ascii_digit() {
436 let mut power = 0i64;
437 while at(k).is_ascii_digit() {
438 power = (power * 10 + i64::from(at(k) - b'0')).min(1 << 40);
439 k += 1;
440 }
441 exp += if negative { -power } else { power };
442 j = k;
443 }
444 }
445 let value: F = from_binary(m, sticky, exp);
446 let range = value.is_infinite() || (value.is_zero() && m != 0);
447 return Some((sign(value), j, range));
448 }
449 return Some((sign(F::from_parts(0, 0)), i + 1, false));
451 }
452
453 let start = i;
454 let mut nonzero = false;
455 let mut digits = 0;
456 while at(i).is_ascii_digit() {
457 nonzero |= at(i) != b'0';
458 digits += 1;
459 i += 1;
460 }
461 if at(i) == b'.' {
462 i += 1;
463 while at(i).is_ascii_digit() {
464 nonzero |= at(i) != b'0';
465 digits += 1;
466 i += 1;
467 }
468 }
469 if digits == 0 {
470 return None;
471 }
472 if matches!(at(i), b'e' | b'E') {
473 let mut k = i + 1;
474 k += usize::from(matches!(at(k), b'+' | b'-'));
475 if at(k).is_ascii_digit() {
476 while at(k).is_ascii_digit() {
477 k += 1;
478 }
479 i = k;
480 }
481 }
482 let text = std::str::from_utf8(&b[start..i]).ok()?;
483 let value: F = text.parse().ok()?;
484 let range = value.is_infinite() || (value.is_zero() && nonzero);
485 Some((sign(value), i, range))
486}
487
488fn float_in<F: Float>(s: &str, type_name: &str) -> Result<F, TypeError> {
489 let b = s.as_bytes();
490 let mut num = 0;
491 while num < b.len() && is_space(b[num]) {
492 num += 1;
493 }
494 let syntax = || TypeError::syntax(type_name, s);
495 let Some((value, len, range)) = strtod::<F>(&b[num..]) else { return Err(syntax()) };
496 if range {
497 return Err(TypeError::new(
498 SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
499 format!("\"{}\" is out of range for type {type_name}", &s[num..num + len]),
500 ));
501 }
502 let mut end = num + len;
503 while end < b.len() && is_space(b[end]) {
504 end += 1;
505 }
506 if end != b.len() {
507 return Err(syntax());
508 }
509 Ok(value)
510}
511
512pub fn float8_in(s: &str) -> Result<f64, TypeError> {
514 float_in(s, "double precision")
515}
516
517pub fn float4_in(s: &str) -> Result<f32, TypeError> {
519 float_in(s, "real")
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525
526 fn out8(v: f64, digits: i32) -> String {
527 let mut out = Vec::new();
528 float8_out(v, digits, &mut out);
529 String::from_utf8(out).unwrap()
530 }
531
532 fn out4(v: f32, digits: i32) -> String {
533 let mut out = Vec::new();
534 float4_out(v, digits, &mut out);
535 String::from_utf8(out).unwrap()
536 }
537
538 #[test]
539 fn the_shortest_text_has_the_layout_of_ryu_in_postgres() {
540 for (v, text) in [
541 (1.0, "1"),
542 (-1.5, "-1.5"),
543 (0.1, "0.1"),
544 (1e-4, "0.0001"),
545 (1e-5, "1e-05"),
546 (1.5e-5, "1.5e-05"),
547 (1e14, "100000000000000"),
548 (1e15, "1e+15"),
549 (123456789012345680.0, "1.2345678901234568e+17"),
550 (1e100, "1e+100"),
551 (5e-324, "5e-324"),
552 (f64::MAX, "1.7976931348623157e+308"),
553 (0.0, "0"),
554 (-0.0, "-0"),
555 (f64::NAN, "NaN"),
556 (f64::INFINITY, "Infinity"),
557 (f64::NEG_INFINITY, "-Infinity"),
558 ] {
559 assert_eq!(out8(v, 1), text, "{v:e}");
560 }
561 for (v, text) in [
562 (1.0f32, "1"),
563 (0.1, "0.1"),
564 (123456.0, "123456"),
565 (1234567.0, "1.234567e+06"),
566 (1e-5, "1e-05"),
567 (f32::MAX, "3.4028235e+38"),
568 (-0.0, "-0"),
569 ] {
570 assert_eq!(out4(v, 1), text, "{v:e}");
571 }
572 }
573
574 #[test]
576 fn the_shortest_text_is_not_halfway() {
577 for (v, text) in [
578 (1e23, "9.999999999999999e+22"),
579 (9.5e21, "9.500000000000001e+21"),
580 (2e22, "2e+22"),
581 (18014398509481992.0, "1.8014398509481992e+16"),
582 (9007199254740996.0, "9.007199254740996e+15"),
583 ] {
584 assert_eq!(out8(v, 1), text, "{v:e}");
585 }
586 for (v, text) in [
587 (42483247.0f32, "4.2483248e+07"),
588 (84966494.0, "8.4966496e+07"),
589 (16777218.0, "1.6777218e+07"),
590 (33554436.0, "3.3554436e+07"),
591 (16777216.0, "1.6777216e+07"),
592 (33554432.0, "3.3554432e+07"),
593 (1e10, "1e+10"),
594 (1.1754944e-38, "1.1754944e-38"),
595 (-12_890_453.0 / 4.0, "-3.2226132e+06"),
596 (-4_179_433.0 / 8.0, "-522429.12"),
597 (-529_305.0 / 16.0, "-33081.562"),
598 ] {
599 assert_eq!(out4(v, 2), text, "{v:e}");
600 }
601 }
602
603 #[test]
604 fn fewer_digits_use_the_g_format() {
605 assert_eq!(out8(0.1, 0), "0.1");
606 assert_eq!(out8(1.0 / 3.0, 0), "0.333333333333333");
607 assert_eq!(out8(1e15, 0), "1e+15");
608 assert_eq!(out8(123456789012345.0, 0), "123456789012345");
609 assert_eq!(out8(1e-5, 0), "1e-05");
610 assert_eq!(out8(2.5, -14), "2");
611 assert_eq!(out8(-1234.5, -15), "-1e+03");
612 assert_eq!(out8(-0.0, 0), "-0");
613 assert_eq!(out4(0.1, 0), "0.1");
614 assert_eq!(out4(1.0 / 3.0, 0), "0.333333");
615 assert_eq!(out4(1234567.0, 0), "1.23457e+06");
616 }
617
618 #[test]
619 fn the_input_takes_what_strtod_takes() {
620 for (input, value) in [
621 ("1", 1.0),
622 (" -1.5e3 ", -1500.0),
623 (".5", 0.5),
624 ("5.", 5.0),
625 ("1e", f64::NAN),
626 ("0x1p3", 8.0),
627 ("0x.8", 0.5),
628 ("-0X1.8P-1", -0.75),
629 ("0x1.fffffffffffff8p1023", f64::NAN),
630 ("Infinity", f64::INFINITY),
631 ("-inf", f64::NEG_INFINITY),
632 ("+INFINITY", f64::INFINITY),
633 ("1e-310", 1e-310),
634 ("0e999999", 0.0),
635 ] {
636 let got = float8_in(input);
637 if value.is_nan() {
638 assert!(got.is_err(), "{input:?}");
639 } else {
640 assert_eq!(got, Ok(value), "{input:?}");
641 }
642 }
643 assert!(float8_in("nan").unwrap().is_nan());
644 assert!(float8_in("NaN(123)").unwrap().is_nan());
645 assert_eq!(float8_in("0x1.fffffffffffffp1023"), Ok(f64::MAX));
646 assert_eq!(float8_in("0x1p-1074"), Ok(5e-324));
647 assert_eq!(float8_in("0x1.8p-1074"), Ok(1e-323));
648 for input in ["", " ", "x", "1.5x", "infinit", "nan(", "--1", "1e5e"] {
649 let error = float8_in(input).unwrap_err();
650 assert_eq!(error.sqlstate.as_str(), "22P02", "{input:?}");
651 assert_eq!(
652 error.message,
653 format!("invalid input syntax for type double precision: \"{input}\"")
654 );
655 }
656 for (input, number) in
657 [(" 1e400 ", "1e400"), ("-1e-400", "-1e-400"), ("0x1p1024", "0x1p1024")]
658 {
659 let error = float8_in(input).unwrap_err();
660 assert_eq!(error.sqlstate.as_str(), "22003");
661 assert_eq!(
662 error.message,
663 format!("\"{number}\" is out of range for type double precision")
664 );
665 }
666 assert_eq!(
667 float4_in("1e39").unwrap_err().message,
668 "\"1e39\" is out of range for type real"
669 );
670 assert_eq!(float4_in("1e-40"), Ok(1e-40));
671 assert_eq!(float4_in("3.4028235e38"), Ok(f32::MAX));
672 assert_eq!(
673 float4_in("x").unwrap_err().message,
674 "invalid input syntax for type real: \"x\""
675 );
676 }
677}