dtoa.h Source File

dtoa.h Source File#

Composable Kernel: dtoa.h Source File
dtoa.h
Go to the documentation of this file.
1// Tencent is pleased to support the open source community by making RapidJSON available.
2//
3// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
4//
5// Licensed under the MIT License (the "License"); you may not use this file except
6// in compliance with the License. You may obtain a copy of the License at
7//
8// http://opensource.org/licenses/MIT
9//
10// Unless required by applicable law or agreed to in writing, software distributed
11// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12// CONDITIONS OF ANY KIND, either express or implied. See the License for the
13// specific language governing permissions and limitations under the License.
14
15// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
16// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
17// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
18
19#ifndef RAPIDJSON_DTOA_
20#define RAPIDJSON_DTOA_
21
22#include "itoa.h" // GetDigitsLut()
23#include "diyfp.h"
24#include "ieee754.h"
25
27namespace internal {
28
29#ifdef __GNUC__
30RAPIDJSON_DIAG_PUSH
31RAPIDJSON_DIAG_OFF(effc++)
32RAPIDJSON_DIAG_OFF(array - bounds) // some gcc versions generate wrong warnings
33 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
34#endif
35
36inline void
37GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w)
38{
39 while(rest < wp_w && delta - rest >= ten_kappa &&
40 (rest + ten_kappa < wp_w ||
41 wp_w - rest > rest + ten_kappa - wp_w))
42 {
43 buffer[len - 1]--;
44 rest += ten_kappa;
45 }
46}
47
49{
50 // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
51 if(n < 10)
52 return 1;
53 if(n < 100)
54 return 2;
55 if(n < 1000)
56 return 3;
57 if(n < 10000)
58 return 4;
59 if(n < 100000)
60 return 5;
61 if(n < 1000000)
62 return 6;
63 if(n < 10000000)
64 return 7;
65 if(n < 100000000)
66 return 8;
67 // Will not reach 10 digits in DigitGen()
68 // if (n < 1000000000) return 9;
69 // return 10;
70 return 9;
71}
72
73inline void
74DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K)
75{
76 static const uint64_t kPow10[] = {1ULL,
77 10ULL,
78 100ULL,
79 1000ULL,
80 10000ULL,
81 100000ULL,
82 1000000ULL,
83 10000000ULL,
84 100000000ULL,
85 1000000000ULL,
86 10000000000ULL,
87 100000000000ULL,
88 1000000000000ULL,
89 10000000000000ULL,
90 100000000000000ULL,
91 1000000000000000ULL,
92 10000000000000000ULL,
93 100000000000000000ULL,
94 1000000000000000000ULL,
95 10000000000000000000ULL};
96 const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
97 const DiyFp wp_w = Mp - W;
98 uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
99 uint64_t p2 = Mp.f & (one.f - 1);
100 int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
101 *len = 0;
102
103 while(kappa > 0)
104 {
105 uint32_t d = 0;
106 switch(kappa)
107 {
108 case 9:
109 d = p1 / 100000000;
110 p1 %= 100000000;
111 break;
112 case 8:
113 d = p1 / 10000000;
114 p1 %= 10000000;
115 break;
116 case 7:
117 d = p1 / 1000000;
118 p1 %= 1000000;
119 break;
120 case 6:
121 d = p1 / 100000;
122 p1 %= 100000;
123 break;
124 case 5:
125 d = p1 / 10000;
126 p1 %= 10000;
127 break;
128 case 4:
129 d = p1 / 1000;
130 p1 %= 1000;
131 break;
132 case 3:
133 d = p1 / 100;
134 p1 %= 100;
135 break;
136 case 2:
137 d = p1 / 10;
138 p1 %= 10;
139 break;
140 case 1:
141 d = p1;
142 p1 = 0;
143 break;
144 default:;
145 }
146 if(d || *len)
147 buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
148 kappa--;
149 uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
150 if(tmp <= delta)
151 {
152 *K += kappa;
153 GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f);
154 return;
155 }
156 }
157
158 // kappa = 0
159 for(;;)
160 {
161 p2 *= 10;
162 delta *= 10;
163 char d = static_cast<char>(p2 >> -one.e);
164 if(d || *len)
165 buffer[(*len)++] = static_cast<char>('0' + d);
166 p2 &= one.f - 1;
167 kappa--;
168 if(p2 < delta)
169 {
170 *K += kappa;
171 int index = -kappa;
172 GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0));
173 return;
174 }
175 }
176}
177
178inline void Grisu2(double value, char* buffer, int* length, int* K)
179{
180 const DiyFp v(value);
181 DiyFp w_m, w_p;
182 v.NormalizedBoundaries(&w_m, &w_p);
183
184 const DiyFp c_mk = GetCachedPower(w_p.e, K);
185 const DiyFp W = v.Normalize() * c_mk;
186 DiyFp Wp = w_p * c_mk;
187 DiyFp Wm = w_m * c_mk;
188 Wm.f++;
189 Wp.f--;
190 DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
191}
192
193inline char* WriteExponent(int K, char* buffer)
194{
195 if(K < 0)
196 {
197 *buffer++ = '-';
198 K = -K;
199 }
200
201 if(K >= 100)
202 {
203 *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
204 K %= 100;
205 const char* d = GetDigitsLut() + K * 2;
206 *buffer++ = d[0];
207 *buffer++ = d[1];
208 }
209 else if(K >= 10)
210 {
211 const char* d = GetDigitsLut() + K * 2;
212 *buffer++ = d[0];
213 *buffer++ = d[1];
214 }
215 else
216 *buffer++ = static_cast<char>('0' + static_cast<char>(K));
217
218 return buffer;
219}
220
221inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces)
222{
223 const int kk = length + k; // 10^(kk-1) <= v < 10^kk
224
225 if(0 <= k && kk <= 21)
226 {
227 // 1234e7 -> 12340000000
228 for(int i = length; i < kk; i++)
229 buffer[i] = '0';
230 buffer[kk] = '.';
231 buffer[kk + 1] = '0';
232 return &buffer[kk + 2];
233 }
234 else if(0 < kk && kk <= 21)
235 {
236 // 1234e-2 -> 12.34
237 std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
238 buffer[kk] = '.';
239 if(0 > k + maxDecimalPlaces)
240 {
241 // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
242 // Remove extra trailing zeros (at least one) after truncation.
243 for(int i = kk + maxDecimalPlaces; i > kk + 1; i--)
244 if(buffer[i] != '0')
245 return &buffer[i + 1];
246 return &buffer[kk + 2]; // Reserve one zero
247 }
248 else
249 return &buffer[length + 1];
250 }
251 else if(-6 < kk && kk <= 0)
252 {
253 // 1234e-6 -> 0.001234
254 const int offset = 2 - kk;
255 std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
256 buffer[0] = '0';
257 buffer[1] = '.';
258 for(int i = 2; i < offset; i++)
259 buffer[i] = '0';
260 if(length - kk > maxDecimalPlaces)
261 {
262 // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
263 // Remove extra trailing zeros (at least one) after truncation.
264 for(int i = maxDecimalPlaces + 1; i > 2; i--)
265 if(buffer[i] != '0')
266 return &buffer[i + 1];
267 return &buffer[3]; // Reserve one zero
268 }
269 else
270 return &buffer[length + offset];
271 }
272 else if(kk < -maxDecimalPlaces)
273 {
274 // Truncate to zero
275 buffer[0] = '0';
276 buffer[1] = '.';
277 buffer[2] = '0';
278 return &buffer[3];
279 }
280 else if(length == 1)
281 {
282 // 1e30
283 buffer[1] = 'e';
284 return WriteExponent(kk - 1, &buffer[2]);
285 }
286 else
287 {
288 // 1234e30 -> 1.234e33
289 std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
290 buffer[1] = '.';
291 buffer[length + 1] = 'e';
292 return WriteExponent(kk - 1, &buffer[0 + length + 2]);
293 }
294}
295
296inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324)
297{
298 RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
299 Double d(value);
300 if(d.IsZero())
301 {
302 if(d.Sign())
303 *buffer++ = '-'; // -0.0, Issue #289
304 buffer[0] = '0';
305 buffer[1] = '.';
306 buffer[2] = '0';
307 return &buffer[3];
308 }
309 else
310 {
311 if(value < 0)
312 {
313 *buffer++ = '-';
314 value = -value;
315 }
316 int length, K;
317 Grisu2(value, buffer, &length, &K);
318 return Prettify(buffer, length, K, maxDecimalPlaces);
319 }
320}
321
322#ifdef __GNUC__
323RAPIDJSON_DIAG_POP
324#endif
325
326} // namespace internal
328
329#endif // RAPIDJSON_DTOA_
Definition ieee754.h:24
bool IsZero() const
Definition ieee754.h:50
bool Sign() const
Definition ieee754.h:39
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition rapidjson.h:451
#define RAPIDJSON_NAMESPACE_BEGIN
provide custom rapidjson namespace (opening expression)
Definition rapidjson.h:121
#define RAPIDJSON_NAMESPACE_END
provide custom rapidjson namespace (closing expression)
Definition rapidjson.h:124
Definition allocators.h:459
void GrisuRound(char *buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w)
Definition dtoa.h:37
char * dtoa(double value, char *buffer, int maxDecimalPlaces=324)
Definition dtoa.h:296
void DigitGen(const DiyFp &W, const DiyFp &Mp, uint64_t delta, char *buffer, int *len, int *K)
Definition dtoa.h:74
char * Prettify(char *buffer, int length, int k, int maxDecimalPlaces)
Definition dtoa.h:221
const char * GetDigitsLut()
Definition itoa.h:23
int CountDecimalDigit32(uint32_t n)
Definition dtoa.h:48
char * WriteExponent(int K, char *buffer)
Definition dtoa.h:193
DiyFp GetCachedPower(int e, int *K)
Definition diyfp.h:239
void Grisu2(double value, char *buffer, int *length, int *K)
Definition dtoa.h:178
const GenericPointer< typename T::ValueType > T2 value
Definition pointer.h:1697
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
Definition diyfp.h:49
uint64_t f
Definition diyfp.h:175
DiyFp Normalize() const
Definition diyfp.h:111
void NormalizedBoundaries(DiyFp *minus, DiyFp *plus) const
Definition diyfp.h:130
int e
Definition diyfp.h:176