Skip to main content

yo_common/
dtoa.rs

1//! Redis's double printer, which is Grisu2 by way of fpconv.
2//!
3//! Redis 7 stopped writing doubles with `%.17g` and started calling
4//! `fpconv_dtoa`, a small Grisu2 implementation it vendors under
5//! `deps/fpconv`. Every double a client reads comes out of it: a sorted set
6//! score, an `INCRBYFLOAT` reply, a geo distance, a RESP3 double. So the exact
7//! bytes it produces are part of the protocol, and there are two reasons a
8//! shortest round trip printer from somewhere else does not reproduce them.
9//!
10//! The first is presentation. fpconv switches to an exponent for a large or a
11//! small magnitude and Rust's `Display` never does, so `1e+30` and
12//! `1000000000000000000000000000000` are the same number written two ways and a
13//! client comparing bytes sees two different answers. That part could have been
14//! fixed by reshaping Rust's digits.
15//!
16//! The second is the digits themselves, and it could not. Grisu2 is not always
17//! shortest. It finds the shortest representation for about nineteen values in
18//! twenty and emits one extra digit for the rest, and which values fall in
19//! which set is a property of the algorithm rather than of the number. Rust's
20//! printer is always shortest. So no amount of reformatting closes the gap, and
21//! the only way to answer what Redis answers is to run what Redis runs.
22//!
23//! This is that port, kept deliberately line for line with the C so that the
24//! next time Redis changes it the diff is readable. The one thing that could
25//! not be carried across literally is unsigned overflow: C leaves it wrapping
26//! and Rust panics on it in a debug build, so every place the original relies
27//! on wrapping is spelled out with a `wrapping_` call.
28//!
29//! ----------------------------------------------------------------------------
30//!
31//! Copyright (c) 2021, Redis Labs
32//! Copyright (c) 2013-2019, night-shift <as.smljk at gmail dot com>
33//! Copyright (c) 2009, Florian Loitsch < florian.loitsch at inria dot fr >
34//! All rights reserved.
35//!
36//! Boost Software License - Version 1.0 - August 17th, 2003
37//!
38//! Permission is hereby granted, free of charge, to any person or organization
39//! obtaining a copy of the software and accompanying documentation covered by
40//! this license (the "Software") to use, reproduce, display, distribute,
41//! execute, and transmit the Software, and to prepare derivative works of the
42//! Software, and to permit third-parties to whom the Software is furnished to
43//! do so, all subject to the following:
44//!
45//! The copyright notices in the Software and this entire statement, including
46//! the above license grant, this restriction and the following disclaimer,
47//! must be included in all copies of the Software, in whole or in part, and
48//! all derivative works of the Software, unless such copies or derivative
49//! works are solely in the form of machine-executable object code generated by
50//! a source language processor.
51//!
52//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
54//! FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
55//! SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
56//! FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
57//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
58//! DEALINGS IN THE SOFTWARE.
59
60/// Room for anything [`dtoa`] can write.
61///
62/// The widest output is the plain integer branch at its limit, which is
63/// seventeen digits and seven zeros, and a sign in front of it. The C carries a
64/// twenty four byte buffer for the same thing and relies on its caller passing
65/// something larger, so this is rounded up to a power of two instead.
66pub const MAX: usize = 32;
67
68const FRACMASK: u64 = 0x000F_FFFF_FFFF_FFFF;
69const EXPMASK: u64 = 0x7FF0_0000_0000_0000;
70const HIDDENBIT: u64 = 0x0010_0000_0000_0000;
71const SIGNMASK: u64 = 0x8000_0000_0000_0000;
72const EXPBIAS: i32 = 1023 + 52;
73
74/// The powers of ten the digit generator divides by, largest first.
75const TENS: [u64; 20] = [
76    10_000_000_000_000_000_000,
77    1_000_000_000_000_000_000,
78    100_000_000_000_000_000,
79    10_000_000_000_000_000,
80    1_000_000_000_000_000,
81    100_000_000_000_000,
82    10_000_000_000_000,
83    1_000_000_000_000,
84    100_000_000_000,
85    10_000_000_000,
86    1_000_000_000,
87    100_000_000,
88    10_000_000,
89    1_000_000,
90    100_000,
91    10_000,
92    1_000,
93    100,
94    10,
95    1,
96];
97
98/// A number held as a fraction and a binary exponent, wider than a double.
99///
100/// Grisu works in this form rather than in doubles because it needs the extra
101/// bits to know when it is allowed to stop emitting digits.
102#[derive(Clone, Copy)]
103struct Fp {
104    frac: u64,
105    exp: i32,
106}
107
108const NPOWERS: i32 = 87;
109const STEPPOWERS: i32 = 8;
110const FIRSTPOWER: i32 = -348;
111const EXPMAX: i32 = -32;
112const EXPMIN: i32 = -60;
113
114/// Every eighth power of ten from `10^-348` up, each as the closest `Fp`.
115///
116/// Eight apart rather than one because the algorithm only needs to land inside
117/// a window of binary exponents, not on an exact power, and a table of eighty
118/// seven entries is small enough to stay in cache where a table of seven
119/// hundred would not.
120static POWERS_TEN: [Fp; 87] = [
121    Fp {
122        frac: 18054884314459144840,
123        exp: -1220,
124    },
125    Fp {
126        frac: 13451937075301367670,
127        exp: -1193,
128    },
129    Fp {
130        frac: 10022474136428063862,
131        exp: -1166,
132    },
133    Fp {
134        frac: 14934650266808366570,
135        exp: -1140,
136    },
137    Fp {
138        frac: 11127181549972568877,
139        exp: -1113,
140    },
141    Fp {
142        frac: 16580792590934885855,
143        exp: -1087,
144    },
145    Fp {
146        frac: 12353653155963782858,
147        exp: -1060,
148    },
149    Fp {
150        frac: 18408377700990114895,
151        exp: -1034,
152    },
153    Fp {
154        frac: 13715310171984221708,
155        exp: -1007,
156    },
157    Fp {
158        frac: 10218702384817765436,
159        exp: -980,
160    },
161    Fp {
162        frac: 15227053142812498563,
163        exp: -954,
164    },
165    Fp {
166        frac: 11345038669416679861,
167        exp: -927,
168    },
169    Fp {
170        frac: 16905424996341287883,
171        exp: -901,
172    },
173    Fp {
174        frac: 12595523146049147757,
175        exp: -874,
176    },
177    Fp {
178        frac: 9384396036005875287,
179        exp: -847,
180    },
181    Fp {
182        frac: 13983839803942852151,
183        exp: -821,
184    },
185    Fp {
186        frac: 10418772551374772303,
187        exp: -794,
188    },
189    Fp {
190        frac: 15525180923007089351,
191        exp: -768,
192    },
193    Fp {
194        frac: 11567161174868858868,
195        exp: -741,
196    },
197    Fp {
198        frac: 17236413322193710309,
199        exp: -715,
200    },
201    Fp {
202        frac: 12842128665889583758,
203        exp: -688,
204    },
205    Fp {
206        frac: 9568131466127621947,
207        exp: -661,
208    },
209    Fp {
210        frac: 14257626930069360058,
211        exp: -635,
212    },
213    Fp {
214        frac: 10622759856335341974,
215        exp: -608,
216    },
217    Fp {
218        frac: 15829145694278690180,
219        exp: -582,
220    },
221    Fp {
222        frac: 11793632577567316726,
223        exp: -555,
224    },
225    Fp {
226        frac: 17573882009934360870,
227        exp: -529,
228    },
229    Fp {
230        frac: 13093562431584567480,
231        exp: -502,
232    },
233    Fp {
234        frac: 9755464219737475723,
235        exp: -475,
236    },
237    Fp {
238        frac: 14536774485912137811,
239        exp: -449,
240    },
241    Fp {
242        frac: 10830740992659433045,
243        exp: -422,
244    },
245    Fp {
246        frac: 16139061738043178685,
247        exp: -396,
248    },
249    Fp {
250        frac: 12024538023802026127,
251        exp: -369,
252    },
253    Fp {
254        frac: 17917957937422433684,
255        exp: -343,
256    },
257    Fp {
258        frac: 13349918974505688015,
259        exp: -316,
260    },
261    Fp {
262        frac: 9946464728195732843,
263        exp: -289,
264    },
265    Fp {
266        frac: 14821387422376473014,
267        exp: -263,
268    },
269    Fp {
270        frac: 11042794154864902060,
271        exp: -236,
272    },
273    Fp {
274        frac: 16455045573212060422,
275        exp: -210,
276    },
277    Fp {
278        frac: 12259964326927110867,
279        exp: -183,
280    },
281    Fp {
282        frac: 18268770466636286478,
283        exp: -157,
284    },
285    Fp {
286        frac: 13611294676837538539,
287        exp: -130,
288    },
289    Fp {
290        frac: 10141204801825835212,
291        exp: -103,
292    },
293    Fp {
294        frac: 15111572745182864684,
295        exp: -77,
296    },
297    Fp {
298        frac: 11258999068426240000,
299        exp: -50,
300    },
301    Fp {
302        frac: 16777216000000000000,
303        exp: -24,
304    },
305    Fp {
306        frac: 12500000000000000000,
307        exp: 3,
308    },
309    Fp {
310        frac: 9313225746154785156,
311        exp: 30,
312    },
313    Fp {
314        frac: 13877787807814456755,
315        exp: 56,
316    },
317    Fp {
318        frac: 10339757656912845936,
319        exp: 83,
320    },
321    Fp {
322        frac: 15407439555097886824,
323        exp: 109,
324    },
325    Fp {
326        frac: 11479437019748901445,
327        exp: 136,
328    },
329    Fp {
330        frac: 17105694144590052135,
331        exp: 162,
332    },
333    Fp {
334        frac: 12744735289059618216,
335        exp: 189,
336    },
337    Fp {
338        frac: 9495567745759798747,
339        exp: 216,
340    },
341    Fp {
342        frac: 14149498560666738074,
343        exp: 242,
344    },
345    Fp {
346        frac: 10542197943230523224,
347        exp: 269,
348    },
349    Fp {
350        frac: 15709099088952724970,
351        exp: 295,
352    },
353    Fp {
354        frac: 11704190886730495818,
355        exp: 322,
356    },
357    Fp {
358        frac: 17440603504673385349,
359        exp: 348,
360    },
361    Fp {
362        frac: 12994262207056124023,
363        exp: 375,
364    },
365    Fp {
366        frac: 9681479787123295682,
367        exp: 402,
368    },
369    Fp {
370        frac: 14426529090290212157,
371        exp: 428,
372    },
373    Fp {
374        frac: 10748601772107342003,
375        exp: 455,
376    },
377    Fp {
378        frac: 16016664761464807395,
379        exp: 481,
380    },
381    Fp {
382        frac: 11933345169920330789,
383        exp: 508,
384    },
385    Fp {
386        frac: 17782069995880619868,
387        exp: 534,
388    },
389    Fp {
390        frac: 13248674568444952270,
391        exp: 561,
392    },
393    Fp {
394        frac: 9871031767461413346,
395        exp: 588,
396    },
397    Fp {
398        frac: 14708983551653345445,
399        exp: 614,
400    },
401    Fp {
402        frac: 10959046745042015199,
403        exp: 641,
404    },
405    Fp {
406        frac: 16330252207878254650,
407        exp: 667,
408    },
409    Fp {
410        frac: 12166986024289022870,
411        exp: 694,
412    },
413    Fp {
414        frac: 18130221999122236476,
415        exp: 720,
416    },
417    Fp {
418        frac: 13508068024458167312,
419        exp: 747,
420    },
421    Fp {
422        frac: 10064294952495520794,
423        exp: 774,
424    },
425    Fp {
426        frac: 14996968138956309548,
427        exp: 800,
428    },
429    Fp {
430        frac: 11173611982879273257,
431        exp: 827,
432    },
433    Fp {
434        frac: 16649979327439178909,
435        exp: 853,
436    },
437    Fp {
438        frac: 12405201291620119593,
439        exp: 880,
440    },
441    Fp {
442        frac: 9242595204427927429,
443        exp: 907,
444    },
445    Fp {
446        frac: 13772540099066387757,
447        exp: 933,
448    },
449    Fp {
450        frac: 10261342003245940623,
451        exp: 960,
452    },
453    Fp {
454        frac: 15290591125556738113,
455        exp: 986,
456    },
457    Fp {
458        frac: 11392378155556871081,
459        exp: 1013,
460    },
461    Fp {
462        frac: 16975966327722178521,
463        exp: 1039,
464    },
465    Fp {
466        frac: 12648080533535911531,
467        exp: 1066,
468    },
469];
470
471/// The table entry whose binary exponent brings `exp` into the working window.
472///
473/// The first guess comes from a logarithm and is then walked one entry at a
474/// time, which sounds slack and is not: the guess is within one of the answer
475/// for every input, so the loop runs once or twice and never searches.
476fn find_cachedpow10(exp: i32, k: &mut i32) -> Fp {
477    const ONE_LOG_TEN: f64 = 0.30102999566398114;
478
479    let approx = (-((exp + NPOWERS) as f64) * ONE_LOG_TEN) as i32;
480    let mut idx = (approx - FIRSTPOWER) / STEPPOWERS;
481
482    loop {
483        let current = exp + POWERS_TEN[idx as usize].exp + 64;
484        if current < EXPMIN {
485            idx += 1;
486            continue;
487        }
488        if current > EXPMAX {
489            idx -= 1;
490            continue;
491        }
492        *k = FIRSTPOWER + idx * STEPPOWERS;
493        return POWERS_TEN[idx as usize];
494    }
495}
496
497/// A double taken apart into its stored fraction and exponent.
498fn build_fp(d: f64) -> Fp {
499    let bits = d.to_bits();
500    let mut fp = Fp {
501        frac: bits & FRACMASK,
502        exp: ((bits & EXPMASK) >> 52) as i32,
503    };
504    if fp.exp != 0 {
505        fp.frac += HIDDENBIT;
506        fp.exp -= EXPBIAS;
507    } else {
508        fp.exp = -EXPBIAS + 1;
509    }
510    fp
511}
512
513/// Shifts the fraction up until its top bit is set, which is where the
514/// multiply below is accurate.
515fn normalize(fp: &mut Fp) {
516    while fp.frac & HIDDENBIT == 0 {
517        fp.frac <<= 1;
518        fp.exp -= 1;
519    }
520    let shift = 64 - 52 - 1;
521    fp.frac <<= shift;
522    fp.exp -= shift;
523}
524
525/// The half way points either side of `fp`, which are the edges of the set of
526/// decimals that read back as this exact double.
527fn get_normalized_boundaries(fp: &Fp, lower: &mut Fp, upper: &mut Fp) {
528    upper.frac = (fp.frac << 1) + 1;
529    upper.exp = fp.exp - 1;
530
531    while upper.frac & (HIDDENBIT << 1) == 0 {
532        upper.frac <<= 1;
533        upper.exp -= 1;
534    }
535
536    let u_shift = 64 - 52 - 2;
537    upper.frac <<= u_shift;
538    upper.exp -= u_shift;
539
540    // A power of two has a closer neighbour below it than above it, so its
541    // lower boundary is half a step away rather than a whole one.
542    let l_shift = if fp.frac == HIDDENBIT { 2 } else { 1 };
543
544    lower.frac = (fp.frac << l_shift) - 1;
545    lower.exp = fp.exp - l_shift;
546
547    lower.frac <<= lower.exp - upper.exp;
548    lower.exp = upper.exp;
549}
550
551/// The high sixty four bits of the product, rounded.
552fn multiply(a: &Fp, b: &Fp) -> Fp {
553    const LOMASK: u64 = 0x0000_0000_FFFF_FFFF;
554
555    let ah_bl = (a.frac >> 32).wrapping_mul(b.frac & LOMASK);
556    let al_bh = (a.frac & LOMASK).wrapping_mul(b.frac >> 32);
557    let al_bl = (a.frac & LOMASK).wrapping_mul(b.frac & LOMASK);
558    let ah_bh = (a.frac >> 32).wrapping_mul(b.frac >> 32);
559
560    let mut tmp = (ah_bl & LOMASK)
561        .wrapping_add(al_bh & LOMASK)
562        .wrapping_add(al_bl >> 32);
563    // Round up rather than truncate.
564    tmp = tmp.wrapping_add(1u64 << 31);
565
566    Fp {
567        frac: ah_bh
568            .wrapping_add(ah_bl >> 32)
569            .wrapping_add(al_bh >> 32)
570            .wrapping_add(tmp >> 32),
571        exp: a.exp + b.exp + 64,
572    }
573}
574
575/// Walks the last digit back while a smaller one is still closer to the value.
576fn round_digit(digits: &mut [u8; 18], ndigits: usize, delta: u64, rem: u64, kappa: u64, frac: u64) {
577    let mut rem = rem;
578    while rem < frac
579        && delta.wrapping_sub(rem) >= kappa
580        && (rem.wrapping_add(kappa) < frac
581            || frac.wrapping_sub(rem) > rem.wrapping_add(kappa).wrapping_sub(frac))
582    {
583        digits[ndigits - 1] -= 1;
584        rem = rem.wrapping_add(kappa);
585    }
586}
587
588/// Emits digits from the top down and stops as soon as the number read back
589/// would land inside the boundaries.
590///
591/// The first loop takes the integral part apart with the table of powers, and
592/// the second one multiplies the fractional part by ten a digit at a time. It
593/// is the early return out of either that makes the output short.
594fn generate_digits(fp: &Fp, upper: &Fp, lower: &Fp, digits: &mut [u8; 18], k: &mut i32) -> usize {
595    let wfrac = upper.frac.wrapping_sub(fp.frac);
596    let mut delta = upper.frac.wrapping_sub(lower.frac);
597
598    let shift = (-upper.exp) as u32;
599    let one_frac = 1u64 << shift;
600
601    let mut part1 = upper.frac >> shift;
602    let mut part2 = upper.frac & (one_frac - 1);
603
604    let mut idx = 0usize;
605    let mut kappa: i32 = 10;
606
607    // Starting at 10^9, the largest power the integral part can hold.
608    let mut divp = 10usize;
609    while kappa > 0 {
610        let div = TENS[divp];
611        let digit = part1 / div;
612
613        if digit != 0 || idx != 0 {
614            digits[idx] = b'0' + digit as u8;
615            idx += 1;
616        }
617
618        part1 -= digit * div;
619        kappa -= 1;
620
621        let tmp = (part1.wrapping_shl(shift)).wrapping_add(part2);
622        if tmp <= delta {
623            *k += kappa;
624            round_digit(digits, idx, delta, tmp, div.wrapping_shl(shift), wfrac);
625            return idx;
626        }
627        divp += 1;
628    }
629
630    let mut unit = 18usize;
631    loop {
632        part2 = part2.wrapping_mul(10);
633        delta = delta.wrapping_mul(10);
634        kappa -= 1;
635
636        let digit = part2 >> shift;
637        if digit != 0 || idx != 0 {
638            digits[idx] = b'0' + digit as u8;
639            idx += 1;
640        }
641
642        part2 &= one_frac - 1;
643        if part2 < delta {
644            *k += kappa;
645            round_digit(
646                digits,
647                idx,
648                delta,
649                part2,
650                one_frac,
651                wfrac.wrapping_mul(TENS[unit]),
652            );
653            return idx;
654        }
655        unit -= 1;
656    }
657}
658
659/// The shortest-ish digits of `d` and the power of ten they sit at.
660fn grisu2(d: f64, digits: &mut [u8; 18], k: &mut i32) -> usize {
661    let mut w = build_fp(d);
662
663    let mut lower = Fp { frac: 0, exp: 0 };
664    let mut upper = Fp { frac: 0, exp: 0 };
665    get_normalized_boundaries(&w, &mut lower, &mut upper);
666
667    normalize(&mut w);
668
669    let mut cached_k = 0;
670    let cp = find_cachedpow10(upper.exp, &mut cached_k);
671
672    w = multiply(&w, &cp);
673    upper = multiply(&upper, &cp);
674    lower = multiply(&lower, &cp);
675
676    // Pull the boundaries in by one so a value sitting exactly on one of them
677    // is not claimed by this double.
678    lower.frac = lower.frac.wrapping_add(1);
679    upper.frac = upper.frac.wrapping_sub(1);
680
681    *k = -cached_k;
682
683    generate_digits(&w, &upper, &lower, digits, k)
684}
685
686/// Lays the digits out, choosing between the plain and the exponent form.
687///
688/// This is the part a client sees. The plain integer form is used when the
689/// value needs at most seven trailing zeros, the plain decimal form when it
690/// needs at most six leading zeros or is close enough to one, and the exponent
691/// form otherwise. Note that the exponent is written with a sign and with no
692/// padding, so it is `1e+30` and `1e-7`, which is neither what `%g` writes nor
693/// what Rust writes.
694fn emit_digits(digits: &[u8; 18], ndigits: usize, dest: &mut [u8], k: i32, neg: bool) -> usize {
695    let mut ndigits = ndigits;
696    let mut exp = (k + ndigits as i32 - 1).abs();
697
698    // Plain integer.
699    if k >= 0 && exp < ndigits as i32 + 7 {
700        let zeros = k as usize;
701        dest[..ndigits].copy_from_slice(&digits[..ndigits]);
702        dest[ndigits..ndigits + zeros].fill(b'0');
703        return ndigits + zeros;
704    }
705
706    // Plain decimal.
707    if k < 0 && (k > -7 || exp < 4) {
708        let offset = ndigits as i32 - k.abs();
709        if offset <= 0 {
710            let lead = (-offset) as usize;
711            dest[0] = b'0';
712            dest[1] = b'.';
713            dest[2..2 + lead].fill(b'0');
714            dest[lead + 2..lead + 2 + ndigits].copy_from_slice(&digits[..ndigits]);
715            return ndigits + 2 + lead;
716        }
717        let offset = offset as usize;
718        dest[..offset].copy_from_slice(&digits[..offset]);
719        dest[offset] = b'.';
720        dest[offset + 1..ndigits + 1].copy_from_slice(&digits[offset..ndigits]);
721        return ndigits + 1;
722    }
723
724    // Exponent form. The cap never bites, because the shortest digits of a
725    // double are at most seventeen and this allows seventeen or eighteen, but
726    // it is here because the original has it.
727    ndigits = ndigits.min(18 - usize::from(neg));
728
729    let mut idx = 0usize;
730    dest[idx] = digits[0];
731    idx += 1;
732
733    if ndigits > 1 {
734        dest[idx] = b'.';
735        idx += 1;
736        dest[idx..idx + ndigits - 1].copy_from_slice(&digits[1..ndigits]);
737        idx += ndigits - 1;
738    }
739
740    dest[idx] = b'e';
741    idx += 1;
742    dest[idx] = if k + ndigits as i32 - 1 < 0 {
743        b'-'
744    } else {
745        b'+'
746    };
747    idx += 1;
748
749    let mut cent = 0;
750    if exp > 99 {
751        cent = exp / 100;
752        dest[idx] = b'0' + cent as u8;
753        idx += 1;
754        exp -= cent * 100;
755    }
756    if exp > 9 {
757        let dec = exp / 10;
758        dest[idx] = b'0' + dec as u8;
759        idx += 1;
760        exp -= dec * 10;
761    } else if cent != 0 {
762        dest[idx] = b'0';
763        idx += 1;
764    }
765    dest[idx] = b'0' + (exp % 10) as u8;
766    idx + 1
767}
768
769/// Writes `d` into `dest` exactly as Redis's `fpconv_dtoa` would, and answers
770/// how many bytes it wrote.
771///
772/// `dest` must be at least [`MAX`] long.
773pub fn dtoa(d: f64, dest: &mut [u8]) -> usize {
774    debug_assert!(dest.len() >= MAX);
775
776    let mut len = 0usize;
777    let neg = d.to_bits() & SIGNMASK != 0;
778    if neg {
779        dest[0] = b'-';
780        len = 1;
781    }
782
783    // Zero, the infinities and NaN never reach the digit generator. The sign
784    // has already been written, which is where `-0` and `-inf` come from.
785    if d == 0.0 {
786        dest[len] = b'0';
787        return len + 1;
788    }
789    if d.is_nan() {
790        dest[len..len + 3].copy_from_slice(b"nan");
791        return len + 3;
792    }
793    if d.is_infinite() {
794        dest[len..len + 3].copy_from_slice(b"inf");
795        return len + 3;
796    }
797
798    let mut digits = [0u8; 18];
799    let mut k = 0i32;
800    let ndigits = grisu2(d, &mut digits, &mut k);
801
802    len + emit_digits(&digits, ndigits, &mut dest[len..], k, neg)
803}
804
805#[cfg(test)]
806mod tests {
807    use super::*;
808
809    fn text(d: f64) -> String {
810        let mut buf = [0u8; MAX];
811        let n = dtoa(d, &mut buf);
812        String::from_utf8(buf[..n].to_vec()).expect("digits are ascii")
813    }
814
815    #[test]
816    fn the_two_plain_forms_cover_the_ordinary_range() {
817        // The forms Redis uses for anything a client is likely to send.
818        for (d, want) in [
819            (0.0, "0"),
820            (-0.0, "-0"),
821            (1.0, "1"),
822            (-1.0, "-1"),
823            (0.5, "0.5"),
824            (0.3, "0.3"),
825            (-0.3, "-0.3"),
826            (3.0e3, "3000"),
827            (1.5, "1.5"),
828            (1234.5678, "1234.5678"),
829            (0.001234, "0.001234"),
830            (core::f64::consts::PI, "3.141592653589793"),
831            (0.0001, "0.0001"),
832        ] {
833            assert_eq!(text(d), want, "{d}");
834        }
835    }
836
837    #[test]
838    fn a_large_or_small_magnitude_switches_to_an_exponent() {
839        // The whole reason this port exists: Rust writes every one of these
840        // with all its zeros and Redis does not.
841        for (d, want) in [
842            // Note that this is the printer on its own. A whole number this
843            // size never reaches it from `push_double`, because Redis takes the
844            // integer path for anything up to two to the sixty second first.
845            (1.0e15, "1e+15"),
846            (1.0e30, "1e+30"),
847            (-1.0e30, "-1e+30"),
848            (1.0e-7, "1e-7"),
849            (1.0e19, "1e+19"),
850            (1.0e100, "1e+100"),
851            (1.0e-100, "1e-100"),
852            (5.0e-324, "5e-324"),
853            (f64::MAX, "1.7976931348623157e+308"),
854            (f64::MIN_POSITIVE, "2.2250738585072014e-308"),
855            (1.2345678e-5, "1.2345678e-5"),
856        ] {
857            assert_eq!(text(d), want, "{d}");
858        }
859    }
860
861    #[test]
862    fn the_special_values_are_written_as_words() {
863        assert_eq!(text(f64::INFINITY), "inf");
864        assert_eq!(text(f64::NEG_INFINITY), "-inf");
865        assert_eq!(text(f64::NAN), "nan");
866        assert_eq!(text(-f64::NAN), "-nan");
867    }
868
869    #[test]
870    fn everything_written_reads_back_as_the_same_double() {
871        // Grisu2 is not always shortest, but it is always exact: whatever it
872        // writes has to parse back to the bits it was handed. That is the
873        // property worth checking over a lot of values, because a port that
874        // dropped a digit somewhere would still look right on a hand table.
875        let mut state = 0x2545_F491_4F6C_DD1Du64;
876        let mut checked = 0u32;
877        for _ in 0..200_000 {
878            state ^= state << 13;
879            state ^= state >> 7;
880            state ^= state << 17;
881            let d = f64::from_bits(state);
882            if !d.is_finite() {
883                continue;
884            }
885            let s = text(d);
886            let back: f64 = s.parse().expect("what was written parses");
887            assert_eq!(back.to_bits(), d.to_bits(), "{s}");
888            checked += 1;
889        }
890        assert!(checked > 190_000, "only {checked} finite values");
891    }
892
893    #[test]
894    fn nothing_written_needs_more_room_than_the_buffer() {
895        let mut widest = 0usize;
896        let mut state = 0x9E37_79B9_7F4A_7C15u64;
897        for _ in 0..200_000 {
898            state ^= state << 13;
899            state ^= state >> 7;
900            state ^= state << 17;
901            let d = f64::from_bits(state);
902            if !d.is_finite() {
903                continue;
904            }
905            let mut buf = [0u8; MAX];
906            widest = widest.max(dtoa(d, &mut buf));
907        }
908        // Both edges of the plain integer branch, which is the widest form.
909        for d in [9.999999999999999e23, -9.999999999999999e23] {
910            let mut buf = [0u8; MAX];
911            widest = widest.max(dtoa(d, &mut buf));
912        }
913        assert!(widest <= MAX, "{widest} bytes needs more than {MAX}");
914    }
915}