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
52U_NAMESPACE_BEGIN
53
54namespace double_conversion {
55
56#if 0 // not needed for ICU
57const 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
69bool 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
90void 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
134void 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
182bool 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
220bool 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
256bool 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
311bool 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
369static 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
383void 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
449U_NAMESPACE_END
450#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING
451