dtoa/
dtoa.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6//
7// ---
8//
9// The C++ implementation preserved here in comments is licensed as follows:
10//
11// Tencent is pleased to support the open source community by making RapidJSON
12// available.
13//
14// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All
15// rights reserved.
16//
17// Licensed under the MIT License (the "License"); you may not use this file
18// except in compliance with the License. You may obtain a copy of the License
19// at
20//
21// http://opensource.org/licenses/MIT
22//
23// Unless required by applicable law or agreed to in writing, software
24// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26// License for the specific language governing permissions and limitations under
27// the License.
28
29use core::ptr;
30#[cfg(feature = "no-panic")]
31use no_panic::no_panic;
32
33/*
34inline unsigned CountDecimalDigit32(uint32_t n) {
35    // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
36    if (n < 10) return 1;
37    if (n < 100) return 2;
38    if (n < 1000) return 3;
39    if (n < 10000) return 4;
40    if (n < 100000) return 5;
41    if (n < 1000000) return 6;
42    if (n < 10000000) return 7;
43    if (n < 100000000) return 8;
44    // Will not reach 10 digits in DigitGen()
45    //if (n < 1000000000) return 9;
46    //return 10;
47    return 9;
48}
49*/
50
51#[inline]
52#[cfg_attr(feature = "no-panic", no_panic)]
53pub fn count_decimal_digit32(n: u32) -> usize {
54    if n < 10 {
55        1
56    } else if n < 100 {
57        2
58    } else if n < 1000 {
59        3
60    } else if n < 10000 {
61        4
62    } else if n < 100000 {
63        5
64    } else if n < 1000000 {
65        6
66    } else if n < 10000000 {
67        7
68    } else if n < 100000000 {
69        8
70    }
71    // Will not reach 10 digits in digit_gen()
72    else {
73        9
74    }
75}
76
77/*
78inline char* WriteExponent(int K, char* buffer) {
79    if (K < 0) {
80        *buffer++ = '-';
81        K = -K;
82    }
83
84    if (K >= 100) {
85        *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
86        K %= 100;
87        const char* d = GetDigitsLut() + K * 2;
88        *buffer++ = d[0];
89        *buffer++ = d[1];
90    }
91    else if (K >= 10) {
92        const char* d = GetDigitsLut() + K * 2;
93        *buffer++ = d[0];
94        *buffer++ = d[1];
95    }
96    else
97        *buffer++ = static_cast<char>('0' + static_cast<char>(K));
98
99    return buffer;
100}
101*/
102
103#[inline]
104#[cfg_attr(feature = "no-panic", no_panic)]
105unsafe fn write_exponent(mut k: isize, mut buffer: *mut u8) -> *mut u8 {
106    if k < 0 {
107        *buffer = b'-';
108        buffer = buffer.offset(1);
109        k = -k;
110    }
111
112    if k >= 100 {
113        *buffer = b'0' + (k / 100) as u8;
114        k %= 100;
115        let d = crate::DEC_DIGITS_LUT.as_ptr().offset(k * 2);
116        ptr::copy_nonoverlapping(d, buffer.offset(1), 2);
117        buffer.offset(3)
118    } else if k >= 10 {
119        let d = crate::DEC_DIGITS_LUT.as_ptr().offset(k * 2);
120        ptr::copy_nonoverlapping(d, buffer, 2);
121        buffer.offset(2)
122    } else {
123        *buffer = b'0' + k as u8;
124        buffer.offset(1)
125    }
126}
127
128/*
129inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
130    const int kk = length + k;  // 10^(kk-1) <= v < 10^kk
131*/
132
133#[inline]
134#[cfg_attr(feature = "no-panic", no_panic)]
135pub unsafe fn prettify(buffer: *mut u8, length: isize, k: isize) -> *mut u8 {
136    let kk = length + k; // 10^(kk-1) <= v < 10^kk
137
138    /*
139    if (0 <= k && kk <= 21) {
140        // 1234e7 -> 12340000000
141        for (int i = length; i < kk; i++)
142            buffer[i] = '0';
143        buffer[kk] = '.';
144        buffer[kk + 1] = '0';
145        return &buffer[kk + 2];
146    }
147    */
148    if 0 <= k && kk <= 21 {
149        // 1234e7 -> 12340000000
150        for i in length..kk {
151            *buffer.offset(i) = b'0';
152        }
153        *buffer.offset(kk) = b'.';
154        *buffer.offset(kk + 1) = b'0';
155        buffer.offset(kk + 2)
156    }
157    /*
158    else if (0 < kk && kk <= 21) {
159        // 1234e-2 -> 12.34
160        std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
161        buffer[kk] = '.';
162        if (0 > k + maxDecimalPlaces) {
163            // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
164            // Remove extra trailing zeros (at least one) after truncation.
165            for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
166                if (buffer[i] != '0')
167                    return &buffer[i + 1];
168            return &buffer[kk + 2]; // Reserve one zero
169        }
170        else
171            return &buffer[length + 1];
172    }
173    */
174    else if 0 < kk && kk <= 21 {
175        // 1234e-2 -> 12.34
176        ptr::copy(
177            buffer.offset(kk),
178            buffer.offset(kk + 1),
179            (length - kk) as usize,
180        );
181        *buffer.offset(kk) = b'.';
182        if 0 > k + crate::MAX_DECIMAL_PLACES {
183            // When MAX_DECIMAL_PLACES = 2, 1.2345 -> 1.23, 1.102 -> 1.1
184            // Remove extra trailing zeros (at least one) after truncation.
185            for i in (kk + 2..kk + crate::MAX_DECIMAL_PLACES + 1).rev() {
186                if *buffer.offset(i) != b'0' {
187                    return buffer.offset(i + 1);
188                }
189            }
190            buffer.offset(kk + 2) // Reserve one zero
191        } else {
192            buffer.offset(length + 1)
193        }
194    }
195    /*
196    else if (-6 < kk && kk <= 0) {
197        // 1234e-6 -> 0.001234
198        const int offset = 2 - kk;
199        std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
200        buffer[0] = '0';
201        buffer[1] = '.';
202        for (int i = 2; i < offset; i++)
203            buffer[i] = '0';
204        if (length - kk > maxDecimalPlaces) {
205            // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
206            // Remove extra trailing zeros (at least one) after truncation.
207            for (int i = maxDecimalPlaces + 1; i > 2; i--)
208                if (buffer[i] != '0')
209                    return &buffer[i + 1];
210            return &buffer[3]; // Reserve one zero
211        }
212        else
213            return &buffer[length + offset];
214    }
215    */
216    else if -6 < kk && kk <= 0 {
217        // 1234e-6 -> 0.001234
218        let offset = 2 - kk;
219        ptr::copy(buffer, buffer.offset(offset), length as usize);
220        *buffer = b'0';
221        *buffer.offset(1) = b'.';
222        for i in 2..offset {
223            *buffer.offset(i) = b'0';
224        }
225        if length - kk > crate::MAX_DECIMAL_PLACES {
226            // When MAX_DECIMAL_PLACES = 2, 0.123 -> 0.12, 0.102 -> 0.1
227            // Remove extra trailing zeros (at least one) after truncation.
228            for i in (3..crate::MAX_DECIMAL_PLACES + 2).rev() {
229                if *buffer.offset(i) != b'0' {
230                    return buffer.offset(i + 1);
231                }
232            }
233            buffer.offset(3) // Reserve one zero
234        } else {
235            buffer.offset(length + offset)
236        }
237    }
238    /*
239    else if (kk < -maxDecimalPlaces) {
240        // Truncate to zero
241        buffer[0] = '0';
242        buffer[1] = '.';
243        buffer[2] = '0';
244        return &buffer[3];
245    }
246    */
247    else if kk < -crate::MAX_DECIMAL_PLACES {
248        *buffer = b'0';
249        *buffer.offset(1) = b'.';
250        *buffer.offset(2) = b'0';
251        buffer.offset(3)
252    }
253    /*
254    else if (length == 1) {
255        // 1e30
256        buffer[1] = 'e';
257        return WriteExponent(kk - 1, &buffer[2]);
258    }
259    */
260    else if length == 1 {
261        // 1e30
262        *buffer.offset(1) = b'e';
263        write_exponent(kk - 1, buffer.offset(2))
264    }
265    /*
266    else {
267        // 1234e30 -> 1.234e33
268        std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
269        buffer[1] = '.';
270        buffer[length + 1] = 'e';
271        return WriteExponent(kk - 1, &buffer[0 + length + 2]);
272    }
273    */
274    else {
275        // 1234e30 -> 1.234e33
276        ptr::copy(buffer.offset(1), buffer.offset(2), (length - 1) as usize);
277        *buffer.offset(1) = b'.';
278        *buffer.offset(length + 1) = b'e';
279        write_exponent(kk - 1, buffer.offset(length + 2))
280    }
281}
282
283macro_rules! dtoa {
284    (
285        floating_type: $fty:ty,
286        significand_type: $sigty:ty,
287        exponent_type: $expty:ty,
288        $($diyfp_param:ident: $diyfp_value:tt,)*
289    ) => {
290        diyfp! {
291            floating_type: $fty,
292            significand_type: $sigty,
293            exponent_type: $expty,
294            $($diyfp_param: $diyfp_value,)*
295        };
296
297        /*
298        inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
299            while (rest < wp_w && delta - rest >= ten_kappa &&
300                (rest + ten_kappa < wp_w ||  /// closer
301                    wp_w - rest > rest + ten_kappa - wp_w)) {
302                buffer[len - 1]--;
303                rest += ten_kappa;
304            }
305        }
306        */
307
308        #[inline]
309        #[cfg_attr(feature = "no-panic", no_panic)]
310        unsafe fn grisu_round(buffer: *mut u8, len: isize, delta: $sigty, mut rest: $sigty, ten_kappa: $sigty, wp_w: $sigty) {
311            while rest < wp_w && delta - rest >= ten_kappa &&
312                (rest + ten_kappa < wp_w || // closer
313                    wp_w - rest > rest + ten_kappa - wp_w) {
314                *buffer.offset(len - 1) -= 1;
315                rest += ten_kappa;
316            }
317        }
318
319        /*
320        inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
321            static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
322            const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
323            const DiyFp wp_w = Mp - W;
324            uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
325            uint64_t p2 = Mp.f & (one.f - 1);
326            unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
327            *len = 0;
328        */
329
330        // Returns length and k.
331        #[inline]
332        #[cfg_attr(feature = "no-panic", no_panic)]
333        unsafe fn digit_gen(w: DiyFp, mp: DiyFp, mut delta: $sigty, buffer: *mut u8, mut k: isize) -> (isize, isize) {
334            static POW10: [$sigty; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 ];
335            let one = DiyFp::new(1 << -mp.e, mp.e);
336            let wp_w = mp - w;
337            let mut p1 = (mp.f >> -one.e) as u32;
338            let mut p2 = mp.f & (one.f - 1);
339            let mut kappa = dtoa::count_decimal_digit32(p1); // kappa in [0, 9]
340            let mut len = 0;
341
342            /*
343            while (kappa > 0) {
344                uint32_t d = 0;
345                switch (kappa) {
346                    case  9: d = p1 /  100000000; p1 %=  100000000; break;
347                    case  8: d = p1 /   10000000; p1 %=   10000000; break;
348                    case  7: d = p1 /    1000000; p1 %=    1000000; break;
349                    case  6: d = p1 /     100000; p1 %=     100000; break;
350                    case  5: d = p1 /      10000; p1 %=      10000; break;
351                    case  4: d = p1 /       1000; p1 %=       1000; break;
352                    case  3: d = p1 /        100; p1 %=        100; break;
353                    case  2: d = p1 /         10; p1 %=         10; break;
354                    case  1: d = p1;              p1 =           0; break;
355                    default:;
356                }
357                if (d || *len)
358                    buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
359                kappa--;
360                uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
361                if (tmp <= delta) {
362                    *K += kappa;
363                    GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
364                    return;
365                }
366            }
367            */
368            while kappa > 0 {
369                let mut d = 0u32;
370                match kappa {
371                    9 => { d = p1 /  100000000; p1 %=  100000000; }
372                    8 => { d = p1 /   10000000; p1 %=   10000000; }
373                    7 => { d = p1 /    1000000; p1 %=    1000000; }
374                    6 => { d = p1 /     100000; p1 %=     100000; }
375                    5 => { d = p1 /      10000; p1 %=      10000; }
376                    4 => { d = p1 /       1000; p1 %=       1000; }
377                    3 => { d = p1 /        100; p1 %=        100; }
378                    2 => { d = p1 /         10; p1 %=         10; }
379                    1 => { d = p1;              p1 =           0; }
380                    _ => {}
381                }
382                if d != 0 || len != 0 {
383                    *buffer.offset(len) = b'0' + d as u8;
384                    len += 1;
385                }
386                kappa -= 1;
387                let tmp = ((p1 as $sigty) << -one.e) + p2;
388                if tmp <= delta {
389                    k += kappa as isize;
390                    grisu_round(buffer, len, delta, tmp, *POW10.get_unchecked(kappa) << -one.e, wp_w.f);
391                    return (len, k);
392                }
393            }
394
395            // kappa = 0
396            /*
397            for (;;) {
398                p2 *= 10;
399                delta *= 10;
400                char d = static_cast<char>(p2 >> -one.e);
401                if (d || *len)
402                    buffer[(*len)++] = static_cast<char>('0' + d);
403                p2 &= one.f - 1;
404                kappa--;
405                if (p2 < delta) {
406                    *K += kappa;
407                    int index = -static_cast<int>(kappa);
408                    GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
409                    return;
410                }
411            }
412            */
413            loop {
414                p2 *= 10;
415                delta *= 10;
416                let d = (p2 >> -one.e) as u8;
417                if d != 0 || len != 0 {
418                    *buffer.offset(len) = b'0' + d;
419                    len += 1;
420                }
421                p2 &= one.f - 1;
422                kappa = kappa.wrapping_sub(1);
423                if p2 < delta {
424                    k += kappa as isize;
425                    let index = -(kappa as isize);
426                    grisu_round(
427                        buffer,
428                        len,
429                        delta,
430                        p2,
431                        one.f,
432                        wp_w.f * if index < 9 {
433                            *POW10.get_unchecked(-(kappa as isize) as usize)
434                        } else {
435                            0
436                        },
437                    );
438                    return (len, k);
439                }
440            }
441        }
442
443        /*
444        inline void Grisu2(double value, char* buffer, int* length, int* K) {
445            const DiyFp v(value);
446            DiyFp w_m, w_p;
447            v.NormalizedBoundaries(&w_m, &w_p);
448
449            const DiyFp c_mk = GetCachedPower(w_p.e, K);
450            const DiyFp W = v.Normalize() * c_mk;
451            DiyFp Wp = w_p * c_mk;
452            DiyFp Wm = w_m * c_mk;
453            Wm.f++;
454            Wp.f--;
455            DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
456        }
457        */
458
459        // Returns length and k.
460        #[inline]
461        #[cfg_attr(feature = "no-panic", no_panic)]
462        unsafe fn grisu2(value: $fty, buffer: *mut u8) -> (isize, isize) {
463            let v = DiyFp::from(value);
464            let (w_m, w_p) = v.normalized_boundaries();
465
466            let (c_mk, k) = get_cached_power(w_p.e);
467            let w = v.normalize() * c_mk;
468            let mut wp = w_p * c_mk;
469            let mut wm = w_m * c_mk;
470            wm.f += 1;
471            wp.f -= 1;
472            digit_gen(w, wp, wp.f - wm.f, buffer, k)
473        }
474
475        /*
476        inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
477            RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
478            Double d(value);
479            if (d.IsZero()) {
480                if (d.Sign())
481                    *buffer++ = '-';     // -0.0, Issue #289
482                buffer[0] = '0';
483                buffer[1] = '.';
484                buffer[2] = '0';
485                return &buffer[3];
486            }
487            else {
488                if (value < 0) {
489                    *buffer++ = '-';
490                    value = -value;
491                }
492                int length, K;
493                Grisu2(value, buffer, &length, &K);
494                return Prettify(buffer, length, K, maxDecimalPlaces);
495            }
496        }
497        */
498
499        #[inline]
500        #[cfg_attr(feature = "no-panic", no_panic)]
501        unsafe fn dtoa(buf: &mut Buffer, mut value: $fty) -> &str {
502            if value == 0.0 {
503                if value.is_sign_negative() {
504                    "-0.0"
505                } else {
506                    "0.0"
507                }
508            } else {
509                let start = buf.bytes.as_mut_ptr() as *mut u8;
510                let mut buf_ptr = start;
511                if value < 0.0 {
512                    *buf_ptr = b'-';
513                    buf_ptr = buf_ptr.offset(1);
514                    value = -value;
515                }
516                let (length, k) = grisu2(value, buf_ptr);
517                let end = dtoa::prettify(buf_ptr, length, k);
518                let len = end as usize - start as usize;
519                str::from_utf8_unchecked(slice::from_raw_parts(start, len))
520            }
521        }
522    };
523}