Skip to content

Commit 0bd125e

Browse files
committed
ICU-21480 Update double-conversion
1 parent 8c9c14e commit 0bd125e

15 files changed

+677
-99
lines changed

icu4c/source/i18n/double-conversion-double-to-string.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,19 @@ void DoubleToStringConverter::CreateExponentialRepresentation(
107107
result_builder->AddCharacter('+');
108108
}
109109
}
110-
if (exponent == 0) {
111-
result_builder->AddCharacter('0');
112-
return;
113-
}
114110
DOUBLE_CONVERSION_ASSERT(exponent < 1e4);
115111
// Changing this constant requires updating the comment of DoubleToStringConverter constructor
116112
const int kMaxExponentLength = 5;
117113
char buffer[kMaxExponentLength + 1];
118114
buffer[kMaxExponentLength] = '\0';
119115
int first_char_pos = kMaxExponentLength;
120-
while (exponent > 0) {
121-
buffer[--first_char_pos] = '0' + (exponent % 10);
122-
exponent /= 10;
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+
}
123123
}
124124
// Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength)
125125
// For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2
@@ -342,9 +342,21 @@ bool DoubleToStringConverter::ToPrecision(double value,
342342
int exponent = decimal_point - 1;
343343

344344
int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
345-
if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
345+
bool as_exponential =
346+
(-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
346347
(decimal_point - precision + extra_zero >
347-
max_trailing_padding_zeroes_in_precision_mode_)) {
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) {
348360
// Fill buffer to contain 'precision' digits.
349361
// Usually the buffer is already at the correct length, but 'DoubleToAscii'
350362
// is allowed to return less characters.

icu4c/source/i18n/double-conversion-double-to-string.h

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,11 @@ namespace double_conversion {
4848

4949
class DoubleToStringConverter {
5050
public:
51-
#if 0 // not needed for ICU
5251
// When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint
5352
// or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the
5453
// function returns false.
5554
static const int kMaxFixedDigitsBeforePoint = 60;
56-
static const int kMaxFixedDigitsAfterPoint = 60;
55+
static const int kMaxFixedDigitsAfterPoint = 100;
5756

5857
// When calling ToExponential with a requested_digits
5958
// parameter > kMaxExponentialDigits then the function returns false.
@@ -65,12 +64,36 @@ class DoubleToStringConverter {
6564
static const int kMinPrecisionDigits = 1;
6665
static const int kMaxPrecisionDigits = 120;
6766

67+
// The maximal number of digits that are needed to emit a double in base 10.
68+
// A higher precision can be achieved by using more digits, but the shortest
69+
// accurate representation of any double will never use more digits than
70+
// kBase10MaximalLength.
71+
// Note that DoubleToAscii null-terminates its input. So the given buffer
72+
// should be at least kBase10MaximalLength + 1 characters long.
73+
static const int kBase10MaximalLength = 17;
74+
75+
// The maximal number of digits that are needed to emit a single in base 10.
76+
// A higher precision can be achieved by using more digits, but the shortest
77+
// accurate representation of any single will never use more digits than
78+
// kBase10MaximalLengthSingle.
79+
static const int kBase10MaximalLengthSingle = 9;
80+
81+
// The length of the longest string that 'ToShortest' can produce when the
82+
// converter is instantiated with EcmaScript defaults (see
83+
// 'EcmaScriptConverter')
84+
// This value does not include the trailing '\0' character.
85+
// This amount of characters is needed for negative values that hit the
86+
// 'decimal_in_shortest_low' limit. For example: "-0.0000033333333333333333"
87+
static const int kMaxCharsEcmaScriptShortest = 25;
88+
89+
#if 0 // not needed for ICU
6890
enum Flags {
6991
NO_FLAGS = 0,
7092
EMIT_POSITIVE_EXPONENT_SIGN = 1,
7193
EMIT_TRAILING_DECIMAL_POINT = 2,
7294
EMIT_TRAILING_ZERO_AFTER_POINT = 4,
73-
UNIQUE_ZERO = 8
95+
UNIQUE_ZERO = 8,
96+
NO_TRAILING_ZERO = 16
7497
};
7598

7699
// Flags should be a bit-or combination of the possible Flags-enum.
@@ -82,9 +105,13 @@ class DoubleToStringConverter {
82105
// Example: 2345.0 is converted to "2345.".
83106
// - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point
84107
// emits a trailing '0'-character. This flag requires the
85-
// EXMIT_TRAILING_DECIMAL_POINT flag.
108+
// EMIT_TRAILING_DECIMAL_POINT flag.
86109
// Example: 2345.0 is converted to "2345.0".
87110
// - UNIQUE_ZERO: "-0.0" is converted to "0.0".
111+
// - NO_TRAILING_ZERO: Trailing zeros are removed from the fractional portion
112+
// of the result in precision mode. Matches printf's %g.
113+
// When EMIT_TRAILING_ZERO_AFTER_POINT is also given, one trailing zero is
114+
// preserved.
88115
//
89116
// Infinity symbol and nan_symbol provide the string representation for these
90117
// special values. If the string is NULL and the special value is encountered
@@ -152,6 +179,14 @@ class DoubleToStringConverter {
152179
}
153180

154181
// Returns a converter following the EcmaScript specification.
182+
//
183+
// Flags: UNIQUE_ZERO and EMIT_POSITIVE_EXPONENT_SIGN.
184+
// Special values: "Infinity" and "NaN".
185+
// Lower case 'e' for exponential values.
186+
// decimal_in_shortest_low: -6
187+
// decimal_in_shortest_high: 21
188+
// max_leading_padding_zeroes_in_precision_mode: 6
189+
// max_trailing_padding_zeroes_in_precision_mode: 0
155190
static const DoubleToStringConverter& EcmaScriptConverter();
156191

157192
// Computes the shortest string of digits that correctly represent the input
@@ -177,6 +212,21 @@ class DoubleToStringConverter {
177212
// Returns true if the conversion succeeds. The conversion always succeeds
178213
// except when the input value is special and no infinity_symbol or
179214
// nan_symbol has been given to the constructor.
215+
//
216+
// The length of the longest result is the maximum of the length of the
217+
// following string representations (each with possible examples):
218+
// - NaN and negative infinity: "NaN", "-Infinity", "-inf".
219+
// - -10^(decimal_in_shortest_high - 1):
220+
// "-100000000000000000000", "-1000000000000000.0"
221+
// - the longest string in range [0; -10^decimal_in_shortest_low]. Generally,
222+
// this string is 3 + kBase10MaximalLength - decimal_in_shortest_low.
223+
// (Sign, '0', decimal point, padding zeroes for decimal_in_shortest_low,
224+
// and the significant digits).
225+
// "-0.0000033333333333333333", "-0.0012345678901234567"
226+
// - the longest exponential representation. (A negative number with
227+
// kBase10MaximalLength significant digits).
228+
// "-1.7976931348623157e+308", "-1.7976931348623157E308"
229+
// In addition, the buffer must be able to hold the trailing '\0' character.
180230
bool ToShortest(double value, StringBuilder* result_builder) const {
181231
return ToShortestIeeeNumber(value, result_builder, SHORTEST);
182232
}
@@ -217,9 +267,11 @@ class DoubleToStringConverter {
217267
// been provided to the constructor,
218268
// - 'value' > 10^kMaxFixedDigitsBeforePoint, or
219269
// - 'requested_digits' > kMaxFixedDigitsAfterPoint.
220-
// The last two conditions imply that the result will never contain more than
221-
// 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters
270+
// The last two conditions imply that the result for non-special values never
271+
// contains more than
272+
// 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters
222273
// (one additional character for the sign, and one for the decimal point).
274+
// In addition, the buffer must be able to hold the trailing '\0' character.
223275
bool ToFixed(double value,
224276
int requested_digits,
225277
StringBuilder* result_builder) const;
@@ -248,14 +300,17 @@ class DoubleToStringConverter {
248300
// - the input value is special and no infinity_symbol or nan_symbol has
249301
// been provided to the constructor,
250302
// - 'requested_digits' > kMaxExponentialDigits.
251-
// The last condition implies that the result will never contain more than
303+
//
304+
// The last condition implies that the result never contains more than
252305
// kMaxExponentialDigits + 8 characters (the sign, the digit before the
253306
// decimal point, the decimal point, the exponent character, the
254307
// exponent's sign, and at most 3 exponent digits).
308+
// In addition, the buffer must be able to hold the trailing '\0' character.
255309
bool ToExponential(double value,
256310
int requested_digits,
257311
StringBuilder* result_builder) const;
258312

313+
259314
// Computes 'precision' leading digits of the given 'value' and returns them
260315
// either in exponential or decimal format, depending on
261316
// max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the
@@ -287,9 +342,11 @@ class DoubleToStringConverter {
287342
// been provided to the constructor,
288343
// - precision < kMinPericisionDigits
289344
// - precision > kMaxPrecisionDigits
290-
// The last condition implies that the result will never contain more than
345+
//
346+
// The last condition implies that the result never contains more than
291347
// kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the
292348
// exponent character, the exponent's sign, and at most 3 exponent digits).
349+
// In addition, the buffer must be able to hold the trailing '\0' character.
293350
bool ToPrecision(double value,
294351
int precision,
295352
StringBuilder* result_builder) const;
@@ -310,14 +367,6 @@ class DoubleToStringConverter {
310367
PRECISION
311368
};
312369

313-
// The maximal number of digits that are needed to emit a double in base 10.
314-
// A higher precision can be achieved by using more digits, but the shortest
315-
// accurate representation of any double will never use more digits than
316-
// kBase10MaximalLength.
317-
// Note that DoubleToAscii null-terminates its input. So the given buffer
318-
// should be at least kBase10MaximalLength + 1 characters long.
319-
static const int kBase10MaximalLength = 17;
320-
321370
// Converts the given double 'v' to digit characters. 'v' must not be NaN,
322371
// +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also
323372
// applies to 'v' after it has been casted to a single-precision float. That

icu4c/source/i18n/double-conversion-string-to-double.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@
5151
// ICU PATCH: Wrap in ICU namespace
5252
U_NAMESPACE_BEGIN
5353

54+
#ifdef _MSC_VER
55+
# if _MSC_VER >= 1900
56+
// Fix MSVC >= 2015 (_MSC_VER == 1900) warning
57+
// C4244: 'argument': conversion from 'const uc16' to 'char', possible loss of data
58+
// against Advance and friends, when instantiated with **it as char, not uc16.
59+
__pragma(warning(disable: 4244))
60+
# endif
61+
# if _MSC_VER <= 1700 // VS2012, see IsDecimalDigitForRadix warning fix, below
62+
# define VS2012_RADIXWARN
63+
# endif
64+
#endif
65+
5466
namespace double_conversion {
5567

5668
namespace {
@@ -170,9 +182,9 @@ static double SignedZero(bool sign) {
170182
//
171183
// The function is small and could be inlined, but VS2012 emitted a warning
172184
// because it constant-propagated the radix and concluded that the last
173-
// condition was always true. By moving it into a separate function the
174-
// compiler wouldn't warn anymore.
175-
#ifdef _MSC_VER
185+
// condition was always true. Moving it into a separate function and
186+
// suppressing optimisation keeps the compiler from warning.
187+
#ifdef VS2012_RADIXWARN
176188
#pragma optimize("",off)
177189
static bool IsDecimalDigitForRadix(int c, int radix) {
178190
return '0' <= c && c <= '9' && (c - '0') < radix;
@@ -738,11 +750,17 @@ double StringToDoubleConverter::StringToIeee(
738750
DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize);
739751
buffer[buffer_pos] = '\0';
740752

753+
// Code above ensures there are no leading zeros and the buffer has fewer than
754+
// kMaxSignificantDecimalDigits characters. Trim trailing zeros.
755+
Vector<const char> chars(buffer, buffer_pos);
756+
chars = TrimTrailingZeros(chars);
757+
exponent += buffer_pos - chars.length();
758+
741759
double converted;
742760
if (read_as_double) {
743-
converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
761+
converted = StrtodTrimmed(chars, exponent);
744762
} else {
745-
converted = Strtof(Vector<const char>(buffer, buffer_pos), exponent);
763+
converted = StrtofTrimmed(chars, exponent);
746764
}
747765
*processed_characters_count = static_cast<int>(current - input);
748766
return sign? -converted: converted;

icu4c/source/i18n/double-conversion-strtod.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,6 @@ static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
115115
return Vector<const char>(buffer.start(), 0);
116116
}
117117

118-
119-
static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
120-
for (int i = buffer.length() - 1; i >= 0; --i) {
121-
if (buffer[i] != '0') {
122-
return buffer.SubVector(0, i + 1);
123-
}
124-
}
125-
return Vector<const char>(buffer.start(), 0);
126-
}
127-
128-
129118
static void CutToMaxSignificantDigits(Vector<const char> buffer,
130119
int exponent,
131120
char* significant_buffer,
@@ -216,12 +205,14 @@ static bool DoubleStrtod(Vector<const char> trimmed,
216205
int exponent,
217206
double* result) {
218207
#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
208+
// Avoid "unused parameter" warnings
209+
(void) trimmed;
210+
(void) exponent;
211+
(void) result;
219212
// On x86 the floating-point stack can be 64 or 80 bits wide. If it is
220213
// 80 bits wide (as is the case on Linux) then double-rounding occurs and the
221214
// result is not accurate.
222215
// We know that Windows32 uses 64 bits and is therefore accurate.
223-
// Note that the ARM simulator is compiled for 32bits. It therefore exhibits
224-
// the same problem.
225216
return false;
226217
#else
227218
if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
@@ -473,6 +464,11 @@ static bool IsNonZeroDigit(const char d) {
473464
return ('1' <= d) && (d <= '9');
474465
}
475466

467+
#ifdef __has_cpp_attribute
468+
#if __has_cpp_attribute(maybe_unused)
469+
[[maybe_unused]]
470+
#endif
471+
#endif
476472
static bool AssertTrimmedDigits(const Vector<const char>& buffer) {
477473
for(int i = 0; i < buffer.length(); ++i) {
478474
if(!IsDigit(buffer[i])) {
@@ -545,6 +541,12 @@ float Strtof(Vector<const char> buffer, int exponent) {
545541
TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
546542
&trimmed, &updated_exponent);
547543
exponent = updated_exponent;
544+
return StrtofTrimmed(trimmed, exponent);
545+
}
546+
547+
float StrtofTrimmed(Vector<const char> trimmed, int exponent) {
548+
DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits);
549+
DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed));
548550

549551
double double_guess;
550552
bool is_correct = ComputeGuess(trimmed, exponent, &double_guess);

icu4c/source/i18n/double-conversion-strtod.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,25 @@ double Strtod(Vector<const char> buffer, int exponent);
5454
// contain a dot or a sign. It must not start with '0', and must not be empty.
5555
float Strtof(Vector<const char> buffer, int exponent);
5656

57-
// For special use cases, the heart of the Strtod() function is also available
58-
// separately, it assumes that 'trimmed' is as produced by TrimAndCut(), i.e.
59-
// no leading or trailing zeros, also no lone zero, and not 'too many' digits.
57+
// Same as Strtod, but assumes that 'trimmed' is already trimmed, as if run
58+
// through TrimAndCut. That is, 'trimmed' must have no leading or trailing
59+
// zeros, must not be a lone zero, and must not have 'too many' digits.
6060
double StrtodTrimmed(Vector<const char> trimmed, int exponent);
6161

62+
// Same as Strtof, but assumes that 'trimmed' is already trimmed, as if run
63+
// through TrimAndCut. That is, 'trimmed' must have no leading or trailing
64+
// zeros, must not be a lone zero, and must not have 'too many' digits.
65+
float StrtofTrimmed(Vector<const char> trimmed, int exponent);
66+
67+
inline Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
68+
for (int i = buffer.length() - 1; i >= 0; --i) {
69+
if (buffer[i] != '0') {
70+
return buffer.SubVector(0, i + 1);
71+
}
72+
}
73+
return Vector<const char>(buffer.start(), 0);
74+
}
75+
6276
} // namespace double_conversion
6377

6478
// ICU PATCH: Close ICU namespace

icu4c/source/i18n/double-conversion-utils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ int main(int argc, char** argv) {
118118
defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \
119119
defined(__hppa__) || defined(__ia64__) || \
120120
defined(__mips__) || \
121-
defined(__nios2__) || \
121+
defined(__nios2__) || defined(__ghs) || \
122122
defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \
123123
defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
124124
defined(__sparc__) || defined(__sparc) || defined(__s390__) || \

vendor/double-conversion/upstream/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ _deps
2828
*.kdev4
2929
DartConfiguration.tcl
3030
bazel-*
31-
31+
.cache

0 commit comments

Comments
 (0)