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_ == DOUBLE_CONVERSION_NULLPTR) 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_ == DOUBLE_CONVERSION_NULLPTR) 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 if ((flags_ & EMIT_TRAILING_DECIMAL_POINT_IN_EXPONENTIAL) != 0) {
99 result_builder->AddCharacter('.');
100 if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT_IN_EXPONENTIAL) != 0) {
101 result_builder->AddCharacter('0');
102 }
103 }
104 } else {
105 result_builder->AddCharacter('.');
106 result_builder->AddSubstring(&decimal_digits[1], length-1);
107 }
108 result_builder->AddCharacter(exponent_character_);
109 if (exponent < 0) {
110 result_builder->AddCharacter('-');
111 exponent = -exponent;
112 } else {
113 if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
114 result_builder->AddCharacter('+');
115 }
116 }
117 DOUBLE_CONVERSION_ASSERT(exponent < 1e4);
118 // Changing this constant requires updating the comment of DoubleToStringConverter constructor
119 const int kMaxExponentLength = 5;
120 char buffer[kMaxExponentLength + 1];
121 buffer[kMaxExponentLength] = '\0';
122 int first_char_pos = kMaxExponentLength;
123 if (exponent == 0) {
124 buffer[--first_char_pos] = '0';
125 } else {
126 while (exponent > 0) {
127 buffer[--first_char_pos] = '0' + (exponent % 10);
128 exponent /= 10;
129 }
130 }
131 // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength)
132 // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2
133 while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) {
134 buffer[--first_char_pos] = '0';
135 }
136 result_builder->AddSubstring(&buffer[first_char_pos],
137 kMaxExponentLength - first_char_pos);
138 }
139
140
141 void DoubleToStringConverter::CreateDecimalRepresentation(
142 const char* decimal_digits,
143 int length,
144 int decimal_point,
145 int digits_after_point,
146 StringBuilder* result_builder) const {
147 // Create a representation that is padded with zeros if needed.
148 if (decimal_point <= 0) {
149 // "0.00000decimal_rep" or "0.000decimal_rep00".
150 result_builder->AddCharacter('0');
151 if (digits_after_point > 0) {
152 result_builder->AddCharacter('.');
153 result_builder->AddPadding('0', -decimal_point);
154 DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point));
155 result_builder->AddSubstring(decimal_digits, length);
156 int remaining_digits = digits_after_point - (-decimal_point) - length;
157 result_builder->AddPadding('0', remaining_digits);
158 }
159 } else if (decimal_point >= length) {
160 // "decimal_rep0000.00000" or "decimal_rep.0000".
161 result_builder->AddSubstring(decimal_digits, length);
162 result_builder->AddPadding('0', decimal_point - length);
163 if (digits_after_point > 0) {
164 result_builder->AddCharacter('.');
165 result_builder->AddPadding('0', digits_after_point);
166 }
167 } else {
168 // "decima.l_rep000".
169 DOUBLE_CONVERSION_ASSERT(digits_after_point > 0);
170 result_builder->AddSubstring(decimal_digits, decimal_point);
171 result_builder->AddCharacter('.');
172 DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point);
173 result_builder->AddSubstring(&decimal_digits[decimal_point],
174 length - decimal_point);
175 int remaining_digits = digits_after_point - (length - decimal_point);
176 result_builder->AddPadding('0', remaining_digits);
177 }
178 if (digits_after_point == 0) {
179 if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
180 result_builder->AddCharacter('.');
181 }
182 if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
183 result_builder->AddCharacter('0');
184 }
185 }
186 }
187
188
189 bool DoubleToStringConverter::ToShortestIeeeNumber(
190 double value,
191 StringBuilder* result_builder,
192 DoubleToStringConverter::DtoaMode mode) const {
193 DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE);
194 if (Double(value).IsSpecial()) {
195 return HandleSpecialValues(value, result_builder);
196 }
197
198 int decimal_point;
199 bool sign;
200 const int kDecimalRepCapacity = kBase10MaximalLength + 1;
201 char decimal_rep[kDecimalRepCapacity];
202 int decimal_rep_length;
203
204 DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity,
205 &sign, &decimal_rep_length, &decimal_point);
206
207 bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
208 if (sign && (value != 0.0 || !unique_zero)) {
209 result_builder->AddCharacter('-');
210 }
211
212 int exponent = decimal_point - 1;
213 if ((decimal_in_shortest_low_ <= exponent) &&
214 (exponent < decimal_in_shortest_high_)) {
215 CreateDecimalRepresentation(decimal_rep, decimal_rep_length,
216 decimal_point,
217 (std::max)(0, decimal_rep_length - decimal_point),
218 result_builder);
219 } else {
220 CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
221 result_builder);
222 }
223 return true;
224 }
225
226
227 bool DoubleToStringConverter::ToFixed(double value,
228 int requested_digits,
229 StringBuilder* result_builder) const {
230 DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60);
231 const double kFirstNonFixed = 1e60;
232
233 if (Double(value).IsSpecial()) {
234 return HandleSpecialValues(value, result_builder);
235 }
236
237 if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
238 if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false;
239
240 // Find a sufficiently precise decimal representation of n.
241 int decimal_point;
242 bool sign;
243 // Add space for the '\0' byte.
244 const int kDecimalRepCapacity =
245 kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
246 char decimal_rep[kDecimalRepCapacity];
247 int decimal_rep_length;
248 DoubleToAscii(value, FIXED, requested_digits,
249 decimal_rep, kDecimalRepCapacity,
250 &sign, &decimal_rep_length, &decimal_point);
251
252 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
253 if (sign && (value != 0.0 || !unique_zero)) {
254 result_builder->AddCharacter('-');
255 }
256
257 CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
258 requested_digits, result_builder);
259 return true;
260 }
261
262
263 bool DoubleToStringConverter::ToExponential(
264 double value,
265 int requested_digits,
266 StringBuilder* result_builder) const {
267 if (Double(value).IsSpecial()) {
268 return HandleSpecialValues(value, result_builder);
269 }
270
271 if (requested_digits < -1) return false;
272 if (requested_digits > kMaxExponentialDigits) return false;
273
274 int decimal_point;
275 bool sign;
276 // Add space for digit before the decimal point and the '\0' character.
277 const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
278 DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
279 char decimal_rep[kDecimalRepCapacity];
280 #ifndef NDEBUG
281 // Problem: there is an assert in StringBuilder::AddSubstring() that
282 // will pass this buffer to strlen(), and this buffer is not generally
283 // null-terminated.
284 memset(decimal_rep, 0, sizeof(decimal_rep));
285 #endif
286 int decimal_rep_length;
287
288 if (requested_digits == -1) {
289 DoubleToAscii(value, SHORTEST, 0,
290 decimal_rep, kDecimalRepCapacity,
291 &sign, &decimal_rep_length, &decimal_point);
292 } else {
293 DoubleToAscii(value, PRECISION, requested_digits + 1,
294 decimal_rep, kDecimalRepCapacity,
295 &sign, &decimal_rep_length, &decimal_point);
296 DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1);
297
298 for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
299 decimal_rep[i] = '0';
300 }
301 decimal_rep_length = requested_digits + 1;
302 }
303
304 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
305 if (sign && (value != 0.0 || !unique_zero)) {
306 result_builder->AddCharacter('-');
307 }
308
309 int exponent = decimal_point - 1;
310 CreateExponentialRepresentation(decimal_rep,
311 decimal_rep_length,
312 exponent,
313 result_builder);
314 return true;
315 }
316
317
318 bool DoubleToStringConverter::ToPrecision(double value,
319 int precision,
320 StringBuilder* result_builder) const {
321 if (Double(value).IsSpecial()) {
322 return HandleSpecialValues(value, result_builder);
323 }
324
325 if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
326 return false;
327 }
328
329 // Find a sufficiently precise decimal representation of n.
330 int decimal_point;
331 bool sign;
332 // Add one for the terminating null character.
333 const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
334 char decimal_rep[kDecimalRepCapacity];
335 int decimal_rep_length;
336
337 DoubleToAscii(value, PRECISION, precision,
338 decimal_rep, kDecimalRepCapacity,
339 &sign, &decimal_rep_length, &decimal_point);
340 DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision);
341
342 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
343 if (sign && (value != 0.0 || !unique_zero)) {
344 result_builder->AddCharacter('-');
345 }
346
347 // The exponent if we print the number as x.xxeyyy. That is with the
348 // decimal point after the first digit.
349 int exponent = decimal_point - 1;
350
351 int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
352 bool as_exponential =
353 (-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
354 (decimal_point - precision + extra_zero >
355 max_trailing_padding_zeroes_in_precision_mode_);
356 if ((flags_ & NO_TRAILING_ZERO) != 0) {
357 // Truncate trailing zeros that occur after the decimal point (if exponential,
358 // that is everything after the first digit).
359 int stop = as_exponential ? 1 : std::max(1, decimal_point);
360 while (decimal_rep_length > stop && decimal_rep[decimal_rep_length - 1] == '0') {
361 --decimal_rep_length;
362 }
363 // Clamp precision to avoid the code below re-adding the zeros.
364 precision = std::min(precision, decimal_rep_length);
365 }
366 if (as_exponential) {
367 // Fill buffer to contain 'precision' digits.
368 // Usually the buffer is already at the correct length, but 'DoubleToAscii'
369 // is allowed to return less characters.
370 for (int i = decimal_rep_length; i < precision; ++i) {
371 decimal_rep[i] = '0';
372 }
373
374 CreateExponentialRepresentation(decimal_rep,
375 precision,
376 exponent,
377 result_builder);
378 } else {
379 CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
380 (std::max)(0, precision - decimal_point),
381 result_builder);
382 }
383 return true;
384 }
385 #endif // not needed for ICU
386
387
DtoaToBignumDtoaMode(DoubleToStringConverter::DtoaMode dtoa_mode)388 static BignumDtoaMode DtoaToBignumDtoaMode(
389 DoubleToStringConverter::DtoaMode dtoa_mode) {
390 switch (dtoa_mode) {
391 case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
392 case DoubleToStringConverter::SHORTEST_SINGLE:
393 return BIGNUM_DTOA_SHORTEST_SINGLE;
394 case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
395 case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
396 default:
397 DOUBLE_CONVERSION_UNREACHABLE();
398 }
399 }
400
401
DoubleToAscii(double v,DtoaMode mode,int requested_digits,char * buffer,int buffer_length,bool * sign,int * length,int * point)402 void DoubleToStringConverter::DoubleToAscii(double v,
403 DtoaMode mode,
404 int requested_digits,
405 char* buffer,
406 int buffer_length,
407 bool* sign,
408 int* length,
409 int* point) {
410 Vector<char> vector(buffer, buffer_length);
411 DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
412 DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
413
414 if (Double(v).Sign() < 0) {
415 *sign = true;
416 v = -v;
417 } else {
418 *sign = false;
419 }
420
421 if (mode == PRECISION && requested_digits == 0) {
422 vector[0] = '\0';
423 *length = 0;
424 return;
425 }
426
427 if (v == 0) {
428 vector[0] = '0';
429 vector[1] = '\0';
430 *length = 1;
431 *point = 1;
432 return;
433 }
434
435 bool fast_worked;
436 switch (mode) {
437 case SHORTEST:
438 fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
439 break;
440 #if 0 // not needed for ICU
441 case SHORTEST_SINGLE:
442 fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0,
443 vector, length, point);
444 break;
445 case FIXED:
446 fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
447 break;
448 case PRECISION:
449 fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
450 vector, length, point);
451 break;
452 #endif // not needed for ICU
453 default:
454 fast_worked = false;
455 DOUBLE_CONVERSION_UNREACHABLE();
456 }
457 if (fast_worked) return;
458
459 // If the fast dtoa didn't succeed use the slower bignum version.
460 BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
461 BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
462 vector[*length] = '\0';
463 }
464
465 } // namespace double_conversion
466
467 // ICU PATCH: Close ICU namespace
468 U_NAMESPACE_END
469 #endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING
470