1 | // © 2018 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html |
3 | // |
4 | // From the double-conversion library. Original license: |
5 | // |
6 | // Copyright 2010 the V8 project authors. All rights reserved. |
7 | // Redistribution and use in source and binary forms, with or without |
8 | // modification, are permitted provided that the following conditions are |
9 | // met: |
10 | // |
11 | // * Redistributions of source code must retain the above copyright |
12 | // notice, this list of conditions and the following disclaimer. |
13 | // * Redistributions in binary form must reproduce the above |
14 | // copyright notice, this list of conditions and the following |
15 | // disclaimer in the documentation and/or other materials provided |
16 | // with the distribution. |
17 | // * Neither the name of Google Inc. nor the names of its |
18 | // contributors may be used to endorse or promote products derived |
19 | // from this software without specific prior written permission. |
20 | // |
21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | |
33 | // ICU PATCH: ifdef around UCONFIG_NO_FORMATTING |
34 | #include "unicode/utypes.h" |
35 | #if !UCONFIG_NO_FORMATTING |
36 | |
37 | #include <algorithm> |
38 | #include <climits> |
39 | #include <cmath> |
40 | |
41 | // ICU PATCH: Customize header file paths for ICU. |
42 | // The file fixed-dtoa.h is not needed. |
43 | |
44 | #include "double-conversion-double-to-string.h" |
45 | |
46 | #include "double-conversion-bignum-dtoa.h" |
47 | #include "double-conversion-fast-dtoa.h" |
48 | #include "double-conversion-ieee.h" |
49 | #include "double-conversion-utils.h" |
50 | |
51 | // ICU PATCH: Wrap in ICU namespace |
52 | U_NAMESPACE_BEGIN |
53 | |
54 | namespace double_conversion { |
55 | |
56 | #if 0 // not needed for ICU |
57 | const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { |
58 | int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; |
59 | static DoubleToStringConverter converter(flags, |
60 | "Infinity" , |
61 | "NaN" , |
62 | 'e', |
63 | -6, 21, |
64 | 6, 0); |
65 | return converter; |
66 | } |
67 | |
68 | |
69 | bool DoubleToStringConverter::HandleSpecialValues( |
70 | double value, |
71 | StringBuilder* result_builder) const { |
72 | Double double_inspect(value); |
73 | if (double_inspect.IsInfinite()) { |
74 | if (infinity_symbol_ == NULL) return false; |
75 | if (value < 0) { |
76 | result_builder->AddCharacter('-'); |
77 | } |
78 | result_builder->AddString(infinity_symbol_); |
79 | return true; |
80 | } |
81 | if (double_inspect.IsNan()) { |
82 | if (nan_symbol_ == NULL) return false; |
83 | result_builder->AddString(nan_symbol_); |
84 | return true; |
85 | } |
86 | return false; |
87 | } |
88 | |
89 | |
90 | void DoubleToStringConverter::CreateExponentialRepresentation( |
91 | const char* decimal_digits, |
92 | int length, |
93 | int exponent, |
94 | StringBuilder* result_builder) const { |
95 | DOUBLE_CONVERSION_ASSERT(length != 0); |
96 | result_builder->AddCharacter(decimal_digits[0]); |
97 | if (length != 1) { |
98 | result_builder->AddCharacter('.'); |
99 | result_builder->AddSubstring(&decimal_digits[1], length-1); |
100 | } |
101 | result_builder->AddCharacter(exponent_character_); |
102 | if (exponent < 0) { |
103 | result_builder->AddCharacter('-'); |
104 | exponent = -exponent; |
105 | } else { |
106 | if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { |
107 | result_builder->AddCharacter('+'); |
108 | } |
109 | } |
110 | if (exponent == 0) { |
111 | result_builder->AddCharacter('0'); |
112 | return; |
113 | } |
114 | DOUBLE_CONVERSION_ASSERT(exponent < 1e4); |
115 | // Changing this constant requires updating the comment of DoubleToStringConverter constructor |
116 | const int kMaxExponentLength = 5; |
117 | char buffer[kMaxExponentLength + 1]; |
118 | buffer[kMaxExponentLength] = '\0'; |
119 | int first_char_pos = kMaxExponentLength; |
120 | while (exponent > 0) { |
121 | buffer[--first_char_pos] = '0' + (exponent % 10); |
122 | exponent /= 10; |
123 | } |
124 | // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength) |
125 | // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2 |
126 | while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) { |
127 | buffer[--first_char_pos] = '0'; |
128 | } |
129 | result_builder->AddSubstring(&buffer[first_char_pos], |
130 | kMaxExponentLength - first_char_pos); |
131 | } |
132 | |
133 | |
134 | void DoubleToStringConverter::CreateDecimalRepresentation( |
135 | const char* decimal_digits, |
136 | int length, |
137 | int decimal_point, |
138 | int digits_after_point, |
139 | StringBuilder* result_builder) const { |
140 | // Create a representation that is padded with zeros if needed. |
141 | if (decimal_point <= 0) { |
142 | // "0.00000decimal_rep" or "0.000decimal_rep00". |
143 | result_builder->AddCharacter('0'); |
144 | if (digits_after_point > 0) { |
145 | result_builder->AddCharacter('.'); |
146 | result_builder->AddPadding('0', -decimal_point); |
147 | DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point)); |
148 | result_builder->AddSubstring(decimal_digits, length); |
149 | int remaining_digits = digits_after_point - (-decimal_point) - length; |
150 | result_builder->AddPadding('0', remaining_digits); |
151 | } |
152 | } else if (decimal_point >= length) { |
153 | // "decimal_rep0000.00000" or "decimal_rep.0000". |
154 | result_builder->AddSubstring(decimal_digits, length); |
155 | result_builder->AddPadding('0', decimal_point - length); |
156 | if (digits_after_point > 0) { |
157 | result_builder->AddCharacter('.'); |
158 | result_builder->AddPadding('0', digits_after_point); |
159 | } |
160 | } else { |
161 | // "decima.l_rep000". |
162 | DOUBLE_CONVERSION_ASSERT(digits_after_point > 0); |
163 | result_builder->AddSubstring(decimal_digits, decimal_point); |
164 | result_builder->AddCharacter('.'); |
165 | DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point); |
166 | result_builder->AddSubstring(&decimal_digits[decimal_point], |
167 | length - decimal_point); |
168 | int remaining_digits = digits_after_point - (length - decimal_point); |
169 | result_builder->AddPadding('0', remaining_digits); |
170 | } |
171 | if (digits_after_point == 0) { |
172 | if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { |
173 | result_builder->AddCharacter('.'); |
174 | } |
175 | if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { |
176 | result_builder->AddCharacter('0'); |
177 | } |
178 | } |
179 | } |
180 | |
181 | |
182 | bool DoubleToStringConverter::ToShortestIeeeNumber( |
183 | double value, |
184 | StringBuilder* result_builder, |
185 | DoubleToStringConverter::DtoaMode mode) const { |
186 | DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); |
187 | if (Double(value).IsSpecial()) { |
188 | return HandleSpecialValues(value, result_builder); |
189 | } |
190 | |
191 | int decimal_point; |
192 | bool sign; |
193 | const int kDecimalRepCapacity = kBase10MaximalLength + 1; |
194 | char decimal_rep[kDecimalRepCapacity]; |
195 | int decimal_rep_length; |
196 | |
197 | DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, |
198 | &sign, &decimal_rep_length, &decimal_point); |
199 | |
200 | bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; |
201 | if (sign && (value != 0.0 || !unique_zero)) { |
202 | result_builder->AddCharacter('-'); |
203 | } |
204 | |
205 | int exponent = decimal_point - 1; |
206 | if ((decimal_in_shortest_low_ <= exponent) && |
207 | (exponent < decimal_in_shortest_high_)) { |
208 | CreateDecimalRepresentation(decimal_rep, decimal_rep_length, |
209 | decimal_point, |
210 | (std::max)(0, decimal_rep_length - decimal_point), |
211 | result_builder); |
212 | } else { |
213 | CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, |
214 | result_builder); |
215 | } |
216 | return true; |
217 | } |
218 | |
219 | |
220 | bool DoubleToStringConverter::ToFixed(double value, |
221 | int requested_digits, |
222 | StringBuilder* result_builder) const { |
223 | DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60); |
224 | const double kFirstNonFixed = 1e60; |
225 | |
226 | if (Double(value).IsSpecial()) { |
227 | return HandleSpecialValues(value, result_builder); |
228 | } |
229 | |
230 | if (requested_digits > kMaxFixedDigitsAfterPoint) return false; |
231 | if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; |
232 | |
233 | // Find a sufficiently precise decimal representation of n. |
234 | int decimal_point; |
235 | bool sign; |
236 | // Add space for the '\0' byte. |
237 | const int kDecimalRepCapacity = |
238 | kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; |
239 | char decimal_rep[kDecimalRepCapacity]; |
240 | int decimal_rep_length; |
241 | DoubleToAscii(value, FIXED, requested_digits, |
242 | decimal_rep, kDecimalRepCapacity, |
243 | &sign, &decimal_rep_length, &decimal_point); |
244 | |
245 | bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); |
246 | if (sign && (value != 0.0 || !unique_zero)) { |
247 | result_builder->AddCharacter('-'); |
248 | } |
249 | |
250 | CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, |
251 | requested_digits, result_builder); |
252 | return true; |
253 | } |
254 | |
255 | |
256 | bool DoubleToStringConverter::ToExponential( |
257 | double value, |
258 | int requested_digits, |
259 | StringBuilder* result_builder) const { |
260 | if (Double(value).IsSpecial()) { |
261 | return HandleSpecialValues(value, result_builder); |
262 | } |
263 | |
264 | if (requested_digits < -1) return false; |
265 | if (requested_digits > kMaxExponentialDigits) return false; |
266 | |
267 | int decimal_point; |
268 | bool sign; |
269 | // Add space for digit before the decimal point and the '\0' character. |
270 | const int kDecimalRepCapacity = kMaxExponentialDigits + 2; |
271 | DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength); |
272 | char decimal_rep[kDecimalRepCapacity]; |
273 | #ifndef NDEBUG |
274 | // Problem: there is an assert in StringBuilder::AddSubstring() that |
275 | // will pass this buffer to strlen(), and this buffer is not generally |
276 | // null-terminated. |
277 | memset(decimal_rep, 0, sizeof(decimal_rep)); |
278 | #endif |
279 | int decimal_rep_length; |
280 | |
281 | if (requested_digits == -1) { |
282 | DoubleToAscii(value, SHORTEST, 0, |
283 | decimal_rep, kDecimalRepCapacity, |
284 | &sign, &decimal_rep_length, &decimal_point); |
285 | } else { |
286 | DoubleToAscii(value, PRECISION, requested_digits + 1, |
287 | decimal_rep, kDecimalRepCapacity, |
288 | &sign, &decimal_rep_length, &decimal_point); |
289 | DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1); |
290 | |
291 | for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { |
292 | decimal_rep[i] = '0'; |
293 | } |
294 | decimal_rep_length = requested_digits + 1; |
295 | } |
296 | |
297 | bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); |
298 | if (sign && (value != 0.0 || !unique_zero)) { |
299 | result_builder->AddCharacter('-'); |
300 | } |
301 | |
302 | int exponent = decimal_point - 1; |
303 | CreateExponentialRepresentation(decimal_rep, |
304 | decimal_rep_length, |
305 | exponent, |
306 | result_builder); |
307 | return true; |
308 | } |
309 | |
310 | |
311 | bool DoubleToStringConverter::ToPrecision(double value, |
312 | int precision, |
313 | StringBuilder* result_builder) const { |
314 | if (Double(value).IsSpecial()) { |
315 | return HandleSpecialValues(value, result_builder); |
316 | } |
317 | |
318 | if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { |
319 | return false; |
320 | } |
321 | |
322 | // Find a sufficiently precise decimal representation of n. |
323 | int decimal_point; |
324 | bool sign; |
325 | // Add one for the terminating null character. |
326 | const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; |
327 | char decimal_rep[kDecimalRepCapacity]; |
328 | int decimal_rep_length; |
329 | |
330 | DoubleToAscii(value, PRECISION, precision, |
331 | decimal_rep, kDecimalRepCapacity, |
332 | &sign, &decimal_rep_length, &decimal_point); |
333 | DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision); |
334 | |
335 | bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); |
336 | if (sign && (value != 0.0 || !unique_zero)) { |
337 | result_builder->AddCharacter('-'); |
338 | } |
339 | |
340 | // The exponent if we print the number as x.xxeyyy. That is with the |
341 | // decimal point after the first digit. |
342 | int exponent = decimal_point - 1; |
343 | |
344 | int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; |
345 | if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || |
346 | (decimal_point - precision + extra_zero > |
347 | max_trailing_padding_zeroes_in_precision_mode_)) { |
348 | // Fill buffer to contain 'precision' digits. |
349 | // Usually the buffer is already at the correct length, but 'DoubleToAscii' |
350 | // is allowed to return less characters. |
351 | for (int i = decimal_rep_length; i < precision; ++i) { |
352 | decimal_rep[i] = '0'; |
353 | } |
354 | |
355 | CreateExponentialRepresentation(decimal_rep, |
356 | precision, |
357 | exponent, |
358 | result_builder); |
359 | } else { |
360 | CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, |
361 | (std::max)(0, precision - decimal_point), |
362 | result_builder); |
363 | } |
364 | return true; |
365 | } |
366 | #endif // not needed for ICU |
367 | |
368 | |
369 | static BignumDtoaMode DtoaToBignumDtoaMode( |
370 | DoubleToStringConverter::DtoaMode dtoa_mode) { |
371 | switch (dtoa_mode) { |
372 | case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; |
373 | case DoubleToStringConverter::SHORTEST_SINGLE: |
374 | return BIGNUM_DTOA_SHORTEST_SINGLE; |
375 | case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; |
376 | case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; |
377 | default: |
378 | DOUBLE_CONVERSION_UNREACHABLE(); |
379 | } |
380 | } |
381 | |
382 | |
383 | void DoubleToStringConverter::DoubleToAscii(double v, |
384 | DtoaMode mode, |
385 | int requested_digits, |
386 | char* buffer, |
387 | int buffer_length, |
388 | bool* sign, |
389 | int* length, |
390 | int* point) { |
391 | Vector<char> vector(buffer, buffer_length); |
392 | DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); |
393 | DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); |
394 | |
395 | if (Double(v).Sign() < 0) { |
396 | *sign = true; |
397 | v = -v; |
398 | } else { |
399 | *sign = false; |
400 | } |
401 | |
402 | if (mode == PRECISION && requested_digits == 0) { |
403 | vector[0] = '\0'; |
404 | *length = 0; |
405 | return; |
406 | } |
407 | |
408 | if (v == 0) { |
409 | vector[0] = '0'; |
410 | vector[1] = '\0'; |
411 | *length = 1; |
412 | *point = 1; |
413 | return; |
414 | } |
415 | |
416 | bool fast_worked; |
417 | switch (mode) { |
418 | case SHORTEST: |
419 | fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); |
420 | break; |
421 | #if 0 // not needed for ICU |
422 | case SHORTEST_SINGLE: |
423 | fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, |
424 | vector, length, point); |
425 | break; |
426 | case FIXED: |
427 | fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); |
428 | break; |
429 | case PRECISION: |
430 | fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, |
431 | vector, length, point); |
432 | break; |
433 | #endif // not needed for ICU |
434 | default: |
435 | fast_worked = false; |
436 | DOUBLE_CONVERSION_UNREACHABLE(); |
437 | } |
438 | if (fast_worked) return; |
439 | |
440 | // If the fast dtoa didn't succeed use the slower bignum version. |
441 | BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); |
442 | BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); |
443 | vector[*length] = '\0'; |
444 | } |
445 | |
446 | } // namespace double_conversion |
447 | |
448 | // ICU PATCH: Close ICU namespace |
449 | U_NAMESPACE_END |
450 | #endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING |
451 | |