Coverage Report

Created: 2025-12-07 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/double-conversion-double-to-string.cpp
Line
Count
Source
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
388
static BignumDtoaMode DtoaToBignumDtoaMode(
389
13.1k
    DoubleToStringConverter::DtoaMode dtoa_mode) {
390
13.1k
  switch (dtoa_mode) {
391
13.1k
    case DoubleToStringConverter::SHORTEST:  return BIGNUM_DTOA_SHORTEST;
392
0
    case DoubleToStringConverter::SHORTEST_SINGLE:
393
0
        return BIGNUM_DTOA_SHORTEST_SINGLE;
394
0
    case DoubleToStringConverter::FIXED:     return BIGNUM_DTOA_FIXED;
395
0
    case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
396
0
    default:
397
0
      DOUBLE_CONVERSION_UNREACHABLE();
398
13.1k
  }
399
13.1k
}
400
401
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
407k
                                            int* point) {
410
407k
  Vector<char> vector(buffer, buffer_length);
411
407k
  DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
412
407k
  DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
413
414
407k
  if (Double(v).Sign() < 0) {
415
0
    *sign = true;
416
0
    v = -v;
417
407k
  } else {
418
407k
    *sign = false;
419
407k
  }
420
421
407k
  if (mode == PRECISION && requested_digits == 0) {
422
0
    vector[0] = '\0';
423
0
    *length = 0;
424
0
    return;
425
0
  }
426
427
407k
  if (v == 0) {
428
0
    vector[0] = '0';
429
0
    vector[1] = '\0';
430
0
    *length = 1;
431
0
    *point = 1;
432
0
    return;
433
0
  }
434
435
407k
  bool fast_worked;
436
407k
  switch (mode) {
437
407k
    case SHORTEST:
438
407k
      fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
439
407k
      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
0
    default:
454
0
      fast_worked = false;
455
0
      DOUBLE_CONVERSION_UNREACHABLE();
456
407k
  }
457
407k
  if (fast_worked) return;
458
459
  // If the fast dtoa didn't succeed use the slower bignum version.
460
13.1k
  BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
461
13.1k
  BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
462
13.1k
  vector[*length] = '\0';
463
13.1k
}
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