/src/rapidjson/include/rapidjson/internal/dtoa.h
Line | Count | Source |
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 | | |
26 | | RAPIDJSON_NAMESPACE_BEGIN |
27 | | namespace internal { |
28 | | |
29 | | #ifdef __GNUC__ |
30 | | RAPIDJSON_DIAG_PUSH |
31 | | RAPIDJSON_DIAG_OFF(effc++) |
32 | | RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 |
33 | | #endif |
34 | | |
35 | 18 | inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { |
36 | 18 | while (rest < wp_w && delta - rest >= ten_kappa && |
37 | 0 | (rest + ten_kappa < wp_w || /// closer |
38 | 0 | wp_w - rest > rest + ten_kappa - wp_w)) { |
39 | 0 | buffer[len - 1]--; |
40 | 0 | rest += ten_kappa; |
41 | 0 | } |
42 | 18 | } |
43 | | |
44 | 18 | inline int CountDecimalDigit32(uint32_t n) { |
45 | | // Simple pure C++ implementation was faster than __builtin_clz version in this situation. |
46 | 18 | if (n < 10) return 1; |
47 | 18 | if (n < 100) return 2; |
48 | 18 | if (n < 1000) return 3; |
49 | 18 | if (n < 10000) return 4; |
50 | 18 | if (n < 100000) return 5; |
51 | 6 | if (n < 1000000) return 6; |
52 | 6 | if (n < 10000000) return 7; |
53 | 6 | if (n < 100000000) return 8; |
54 | | // Will not reach 10 digits in DigitGen() |
55 | | //if (n < 1000000000) return 9; |
56 | | //return 10; |
57 | 0 | return 9; |
58 | 6 | } |
59 | | |
60 | 18 | inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { |
61 | 18 | static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, |
62 | 18 | 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, |
63 | 18 | 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, |
64 | 18 | 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, |
65 | 18 | 10000000000000000000ULL }; |
66 | 18 | const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); |
67 | 18 | const DiyFp wp_w = Mp - W; |
68 | 18 | uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e); |
69 | 18 | uint64_t p2 = Mp.f & (one.f - 1); |
70 | 18 | int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] |
71 | 18 | *len = 0; |
72 | | |
73 | 50 | while (kappa > 0) { |
74 | 46 | uint32_t d = 0; |
75 | 46 | switch (kappa) { |
76 | 0 | case 9: d = p1 / 100000000; p1 %= 100000000; break; |
77 | 6 | case 8: d = p1 / 10000000; p1 %= 10000000; break; |
78 | 4 | case 7: d = p1 / 1000000; p1 %= 1000000; break; |
79 | 4 | case 6: d = p1 / 100000; p1 %= 100000; break; |
80 | 16 | case 5: d = p1 / 10000; p1 %= 10000; break; |
81 | 4 | case 4: d = p1 / 1000; p1 %= 1000; break; |
82 | 4 | case 3: d = p1 / 100; p1 %= 100; break; |
83 | 4 | case 2: d = p1 / 10; p1 %= 10; break; |
84 | 4 | case 1: d = p1; p1 = 0; break; |
85 | 0 | default:; |
86 | 46 | } |
87 | 46 | if (d || *len) |
88 | 46 | buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d)); |
89 | 46 | kappa--; |
90 | 46 | uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2; |
91 | 46 | if (tmp <= delta) { |
92 | 14 | *K += kappa; |
93 | 14 | GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); |
94 | 14 | return; |
95 | 14 | } |
96 | 46 | } |
97 | | |
98 | | // kappa = 0 |
99 | 36 | for (;;) { |
100 | 36 | p2 *= 10; |
101 | 36 | delta *= 10; |
102 | 36 | char d = static_cast<char>(p2 >> -one.e); |
103 | 36 | if (d || *len) |
104 | 36 | buffer[(*len)++] = static_cast<char>('0' + d); |
105 | 36 | p2 &= one.f - 1; |
106 | 36 | kappa--; |
107 | 36 | if (p2 < delta) { |
108 | 4 | *K += kappa; |
109 | 4 | int index = -kappa; |
110 | 4 | GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0)); |
111 | 4 | return; |
112 | 4 | } |
113 | 36 | } |
114 | 4 | } |
115 | | |
116 | 18 | inline void Grisu2(double value, char* buffer, int* length, int* K) { |
117 | 18 | const DiyFp v(value); |
118 | 18 | DiyFp w_m, w_p; |
119 | 18 | v.NormalizedBoundaries(&w_m, &w_p); |
120 | | |
121 | 18 | const DiyFp c_mk = GetCachedPower(w_p.e, K); |
122 | 18 | const DiyFp W = v.Normalize() * c_mk; |
123 | 18 | DiyFp Wp = w_p * c_mk; |
124 | 18 | DiyFp Wm = w_m * c_mk; |
125 | 18 | Wm.f++; |
126 | 18 | Wp.f--; |
127 | 18 | DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); |
128 | 18 | } |
129 | | |
130 | 6 | inline char* WriteExponent(int K, char* buffer) { |
131 | 6 | if (K < 0) { |
132 | 6 | *buffer++ = '-'; |
133 | 6 | K = -K; |
134 | 6 | } |
135 | | |
136 | 6 | if (K >= 100) { |
137 | 6 | *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100)); |
138 | 6 | K %= 100; |
139 | 6 | const char* d = GetDigitsLut() + K * 2; |
140 | 6 | *buffer++ = d[0]; |
141 | 6 | *buffer++ = d[1]; |
142 | 6 | } |
143 | 0 | else if (K >= 10) { |
144 | 0 | const char* d = GetDigitsLut() + K * 2; |
145 | 0 | *buffer++ = d[0]; |
146 | 0 | *buffer++ = d[1]; |
147 | 0 | } |
148 | 0 | else |
149 | 0 | *buffer++ = static_cast<char>('0' + static_cast<char>(K)); |
150 | | |
151 | 6 | return buffer; |
152 | 6 | } |
153 | | |
154 | 18 | inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { |
155 | 18 | const int kk = length + k; // 10^(kk-1) <= v < 10^kk |
156 | | |
157 | 18 | if (0 <= k && kk <= 21) { |
158 | | // 1234e7 -> 12340000000 |
159 | 12 | for (int i = length; i < kk; i++) |
160 | 0 | buffer[i] = '0'; |
161 | 12 | buffer[kk] = '.'; |
162 | 12 | buffer[kk + 1] = '0'; |
163 | 12 | return &buffer[kk + 2]; |
164 | 12 | } |
165 | 6 | else if (0 < kk && kk <= 21) { |
166 | | // 1234e-2 -> 12.34 |
167 | 0 | std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk)); |
168 | 0 | buffer[kk] = '.'; |
169 | 0 | if (0 > k + maxDecimalPlaces) { |
170 | | // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 |
171 | | // Remove extra trailing zeros (at least one) after truncation. |
172 | 0 | for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) |
173 | 0 | if (buffer[i] != '0') |
174 | 0 | return &buffer[i + 1]; |
175 | 0 | return &buffer[kk + 2]; // Reserve one zero |
176 | 0 | } |
177 | 0 | else |
178 | 0 | return &buffer[length + 1]; |
179 | 0 | } |
180 | 6 | else if (-6 < kk && kk <= 0) { |
181 | | // 1234e-6 -> 0.001234 |
182 | 0 | const int offset = 2 - kk; |
183 | 0 | std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length)); |
184 | 0 | buffer[0] = '0'; |
185 | 0 | buffer[1] = '.'; |
186 | 0 | for (int i = 2; i < offset; i++) |
187 | 0 | buffer[i] = '0'; |
188 | 0 | if (length - kk > maxDecimalPlaces) { |
189 | | // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 |
190 | | // Remove extra trailing zeros (at least one) after truncation. |
191 | 0 | for (int i = maxDecimalPlaces + 1; i > 2; i--) |
192 | 0 | if (buffer[i] != '0') |
193 | 0 | return &buffer[i + 1]; |
194 | 0 | return &buffer[3]; // Reserve one zero |
195 | 0 | } |
196 | 0 | else |
197 | 0 | return &buffer[length + offset]; |
198 | 0 | } |
199 | 6 | else if (kk < -maxDecimalPlaces) { |
200 | | // Truncate to zero |
201 | 0 | buffer[0] = '0'; |
202 | 0 | buffer[1] = '.'; |
203 | 0 | buffer[2] = '0'; |
204 | 0 | return &buffer[3]; |
205 | 0 | } |
206 | 6 | else if (length == 1) { |
207 | | // 1e30 |
208 | 2 | buffer[1] = 'e'; |
209 | 2 | return WriteExponent(kk - 1, &buffer[2]); |
210 | 2 | } |
211 | 4 | else { |
212 | | // 1234e30 -> 1.234e33 |
213 | 4 | std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1)); |
214 | 4 | buffer[1] = '.'; |
215 | 4 | buffer[length + 1] = 'e'; |
216 | 4 | return WriteExponent(kk - 1, &buffer[0 + length + 2]); |
217 | 4 | } |
218 | 18 | } |
219 | | |
220 | 18 | inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { |
221 | 18 | RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); |
222 | 18 | Double d(value); |
223 | 18 | if (d.IsZero()) { |
224 | 0 | if (d.Sign()) |
225 | 0 | *buffer++ = '-'; // -0.0, Issue #289 |
226 | 0 | buffer[0] = '0'; |
227 | 0 | buffer[1] = '.'; |
228 | 0 | buffer[2] = '0'; |
229 | 0 | return &buffer[3]; |
230 | 0 | } |
231 | 18 | else { |
232 | 18 | if (value < 0) { |
233 | 0 | *buffer++ = '-'; |
234 | 0 | value = -value; |
235 | 0 | } |
236 | 18 | int length, K; |
237 | 18 | Grisu2(value, buffer, &length, &K); |
238 | 18 | return Prettify(buffer, length, K, maxDecimalPlaces); |
239 | 18 | } |
240 | 18 | } |
241 | | |
242 | | #ifdef __GNUC__ |
243 | | RAPIDJSON_DIAG_POP |
244 | | #endif |
245 | | |
246 | | } // namespace internal |
247 | | RAPIDJSON_NAMESPACE_END |
248 | | |
249 | | #endif // RAPIDJSON_DTOA_ |