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}