Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/double-conversion-double-to-string.cpp
Line
Count
Source (jump to first uncovered line)
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
  DOUBLE_CONVERSION_ASSERT(exponent < 1e4);
111
  // Changing this constant requires updating the comment of DoubleToStringConverter constructor
112
  const int kMaxExponentLength = 5;
113
  char buffer[kMaxExponentLength + 1];
114
  buffer[kMaxExponentLength] = '\0';
115
  int first_char_pos = kMaxExponentLength;
116
  if (exponent == 0) {
117
    buffer[--first_char_pos] = '0';
118
  } else {
119
    while (exponent > 0) {
120
      buffer[--first_char_pos] = '0' + (exponent % 10);
121
      exponent /= 10;
122
    }
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
  bool as_exponential =
346
      (-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
347
      (decimal_point - precision + extra_zero >
348
       max_trailing_padding_zeroes_in_precision_mode_);
349
  if ((flags_ & NO_TRAILING_ZERO) != 0) {
350
    // Truncate trailing zeros that occur after the decimal point (if exponential,
351
    // that is everything after the first digit).
352
    int stop = as_exponential ? 1 : std::max(1, decimal_point);
353
    while (decimal_rep_length > stop && decimal_rep[decimal_rep_length - 1] == '0') {
354
      --decimal_rep_length;
355
    }
356
    // Clamp precision to avoid the code below re-adding the zeros.
357
    precision = std::min(precision, decimal_rep_length);
358
  }
359
  if (as_exponential) {
360
    // Fill buffer to contain 'precision' digits.
361
    // Usually the buffer is already at the correct length, but 'DoubleToAscii'
362
    // is allowed to return less characters.
363
    for (int i = decimal_rep_length; i < precision; ++i) {
364
      decimal_rep[i] = '0';
365
    }
366
367
    CreateExponentialRepresentation(decimal_rep,
368
                                    precision,
369
                                    exponent,
370
                                    result_builder);
371
  } else {
372
    CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
373
                                (std::max)(0, precision - decimal_point),
374
                                result_builder);
375
  }
376
  return true;
377
}
378
#endif // not needed for ICU
379
380
381
static BignumDtoaMode DtoaToBignumDtoaMode(
382
0
    DoubleToStringConverter::DtoaMode dtoa_mode) {
383
0
  switch (dtoa_mode) {
384
0
    case DoubleToStringConverter::SHORTEST:  return BIGNUM_DTOA_SHORTEST;
385
0
    case DoubleToStringConverter::SHORTEST_SINGLE:
386
0
        return BIGNUM_DTOA_SHORTEST_SINGLE;
387
0
    case DoubleToStringConverter::FIXED:     return BIGNUM_DTOA_FIXED;
388
0
    case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
389
0
    default:
390
0
      DOUBLE_CONVERSION_UNREACHABLE();
391
0
  }
392
0
}
393
394
395
void DoubleToStringConverter::DoubleToAscii(double v,
396
                                            DtoaMode mode,
397
                                            int requested_digits,
398
                                            char* buffer,
399
                                            int buffer_length,
400
                                            bool* sign,
401
                                            int* length,
402
0
                                            int* point) {
403
0
  Vector<char> vector(buffer, buffer_length);
404
0
  DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
405
0
  DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
406
407
0
  if (Double(v).Sign() < 0) {
408
0
    *sign = true;
409
0
    v = -v;
410
0
  } else {
411
0
    *sign = false;
412
0
  }
413
414
0
  if (mode == PRECISION && requested_digits == 0) {
415
0
    vector[0] = '\0';
416
0
    *length = 0;
417
0
    return;
418
0
  }
419
420
0
  if (v == 0) {
421
0
    vector[0] = '0';
422
0
    vector[1] = '\0';
423
0
    *length = 1;
424
0
    *point = 1;
425
0
    return;
426
0
  }
427
428
0
  bool fast_worked;
429
0
  switch (mode) {
430
0
    case SHORTEST:
431
0
      fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
432
0
      break;
433
#if 0 // not needed for ICU
434
    case SHORTEST_SINGLE:
435
      fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0,
436
                             vector, length, point);
437
      break;
438
    case FIXED:
439
      fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
440
      break;
441
    case PRECISION:
442
      fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
443
                             vector, length, point);
444
      break;
445
#endif // not needed for ICU
446
0
    default:
447
0
      fast_worked = false;
448
0
      DOUBLE_CONVERSION_UNREACHABLE();
449
0
  }
450
0
  if (fast_worked) return;
451
452
  // If the fast dtoa didn't succeed use the slower bignum version.
453
0
  BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
454
0
  BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
455
0
  vector[*length] = '\0';
456
0
}
457
458
}  // namespace double_conversion
459
460
// ICU PATCH: Close ICU namespace
461
U_NAMESPACE_END
462
#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING