1 // Copyright 2010 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include <algorithm>
29 #include <climits>
30 #include <cmath>
31
32 #include "double-to-string.h"
33
34 #include "bignum-dtoa.h"
35 #include "fast-dtoa.h"
36 #include "fixed-dtoa.h"
37 #include "ieee.h"
38 #include "utils.h"
39
40 namespace double_conversion {
41
EcmaScriptConverter()42 const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() {
43 int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN;
44 static DoubleToStringConverter converter(flags,
45 "Infinity",
46 "NaN",
47 'e',
48 -6, 21,
49 6, 0);
50 return converter;
51 }
52
53
HandleSpecialValues(double value,StringBuilder * result_builder) const54 bool DoubleToStringConverter::HandleSpecialValues(
55 double value,
56 StringBuilder* result_builder) const {
57 Double double_inspect(value);
58 if (double_inspect.IsInfinite()) {
59 if (infinity_symbol_ == DOUBLE_CONVERSION_NULLPTR) return false;
60 if (value < 0) {
61 result_builder->AddCharacter('-');
62 }
63 result_builder->AddString(infinity_symbol_);
64 return true;
65 }
66 if (double_inspect.IsNan()) {
67 if (nan_symbol_ == DOUBLE_CONVERSION_NULLPTR) return false;
68 result_builder->AddString(nan_symbol_);
69 return true;
70 }
71 return false;
72 }
73
74
CreateExponentialRepresentation(const char * decimal_digits,int length,int exponent,StringBuilder * result_builder) const75 void DoubleToStringConverter::CreateExponentialRepresentation(
76 const char* decimal_digits,
77 int length,
78 int exponent,
79 StringBuilder* result_builder) const {
80 DOUBLE_CONVERSION_ASSERT(length != 0);
81 result_builder->AddCharacter(decimal_digits[0]);
82 if (length != 1) {
83 result_builder->AddCharacter('.');
84 result_builder->AddSubstring(&decimal_digits[1], length-1);
85 }
86 result_builder->AddCharacter(exponent_character_);
87 if (exponent < 0) {
88 result_builder->AddCharacter('-');
89 exponent = -exponent;
90 } else {
91 if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
92 result_builder->AddCharacter('+');
93 }
94 }
95 DOUBLE_CONVERSION_ASSERT(exponent < 1e4);
96 // Changing this constant requires updating the comment of DoubleToStringConverter constructor
97 const int kMaxExponentLength = 5;
98 char buffer[kMaxExponentLength + 1];
99 buffer[kMaxExponentLength] = '\0';
100 int first_char_pos = kMaxExponentLength;
101 if (exponent == 0) {
102 buffer[--first_char_pos] = '0';
103 } else {
104 while (exponent > 0) {
105 buffer[--first_char_pos] = '0' + (exponent % 10);
106 exponent /= 10;
107 }
108 }
109 // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength)
110 // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2
111 while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) {
112 buffer[--first_char_pos] = '0';
113 }
114 result_builder->AddSubstring(&buffer[first_char_pos],
115 kMaxExponentLength - first_char_pos);
116 }
117
118
CreateDecimalRepresentation(const char * decimal_digits,int length,int decimal_point,int digits_after_point,StringBuilder * result_builder) const119 void DoubleToStringConverter::CreateDecimalRepresentation(
120 const char* decimal_digits,
121 int length,
122 int decimal_point,
123 int digits_after_point,
124 StringBuilder* result_builder) const {
125 // Create a representation that is padded with zeros if needed.
126 if (decimal_point <= 0) {
127 // "0.00000decimal_rep" or "0.000decimal_rep00".
128 result_builder->AddCharacter('0');
129 if (digits_after_point > 0) {
130 result_builder->AddCharacter('.');
131 result_builder->AddPadding('0', -decimal_point);
132 DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point));
133 result_builder->AddSubstring(decimal_digits, length);
134 int remaining_digits = digits_after_point - (-decimal_point) - length;
135 result_builder->AddPadding('0', remaining_digits);
136 }
137 } else if (decimal_point >= length) {
138 // "decimal_rep0000.00000" or "decimal_rep.0000".
139 result_builder->AddSubstring(decimal_digits, length);
140 result_builder->AddPadding('0', decimal_point - length);
141 if (digits_after_point > 0) {
142 result_builder->AddCharacter('.');
143 result_builder->AddPadding('0', digits_after_point);
144 }
145 } else {
146 // "decima.l_rep000".
147 DOUBLE_CONVERSION_ASSERT(digits_after_point > 0);
148 result_builder->AddSubstring(decimal_digits, decimal_point);
149 result_builder->AddCharacter('.');
150 DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point);
151 result_builder->AddSubstring(&decimal_digits[decimal_point],
152 length - decimal_point);
153 int remaining_digits = digits_after_point - (length - decimal_point);
154 result_builder->AddPadding('0', remaining_digits);
155 }
156 if (digits_after_point == 0) {
157 if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
158 result_builder->AddCharacter('.');
159 }
160 if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
161 result_builder->AddCharacter('0');
162 }
163 }
164 }
165
166
ToShortestIeeeNumber(double value,StringBuilder * result_builder,DoubleToStringConverter::DtoaMode mode) const167 bool DoubleToStringConverter::ToShortestIeeeNumber(
168 double value,
169 StringBuilder* result_builder,
170 DoubleToStringConverter::DtoaMode mode) const {
171 DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE);
172 if (Double(value).IsSpecial()) {
173 return HandleSpecialValues(value, result_builder);
174 }
175
176 int decimal_point;
177 bool sign;
178 const int kDecimalRepCapacity = kBase10MaximalLength + 1;
179 char decimal_rep[kDecimalRepCapacity];
180 int decimal_rep_length;
181
182 DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity,
183 &sign, &decimal_rep_length, &decimal_point);
184
185 bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
186 if (sign && (value != 0.0 || !unique_zero)) {
187 result_builder->AddCharacter('-');
188 }
189
190 int exponent = decimal_point - 1;
191 if ((decimal_in_shortest_low_ <= exponent) &&
192 (exponent < decimal_in_shortest_high_)) {
193 CreateDecimalRepresentation(decimal_rep, decimal_rep_length,
194 decimal_point,
195 (std::max)(0, decimal_rep_length - decimal_point),
196 result_builder);
197 } else {
198 CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
199 result_builder);
200 }
201 return true;
202 }
203
204
ToFixed(double value,int requested_digits,StringBuilder * result_builder) const205 bool DoubleToStringConverter::ToFixed(double value,
206 int requested_digits,
207 StringBuilder* result_builder) const {
208 DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60);
209 const double kFirstNonFixed = 1e60;
210
211 if (Double(value).IsSpecial()) {
212 return HandleSpecialValues(value, result_builder);
213 }
214
215 if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
216 if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false;
217
218 // Find a sufficiently precise decimal representation of n.
219 int decimal_point;
220 bool sign;
221 // Add space for the '\0' byte.
222 const int kDecimalRepCapacity =
223 kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
224 char decimal_rep[kDecimalRepCapacity];
225 int decimal_rep_length;
226 DoubleToAscii(value, FIXED, requested_digits,
227 decimal_rep, kDecimalRepCapacity,
228 &sign, &decimal_rep_length, &decimal_point);
229
230 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
231 if (sign && (value != 0.0 || !unique_zero)) {
232 result_builder->AddCharacter('-');
233 }
234
235 CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
236 requested_digits, result_builder);
237 return true;
238 }
239
240
ToExponential(double value,int requested_digits,StringBuilder * result_builder) const241 bool DoubleToStringConverter::ToExponential(
242 double value,
243 int requested_digits,
244 StringBuilder* result_builder) const {
245 if (Double(value).IsSpecial()) {
246 return HandleSpecialValues(value, result_builder);
247 }
248
249 if (requested_digits < -1) return false;
250 if (requested_digits > kMaxExponentialDigits) return false;
251
252 int decimal_point;
253 bool sign;
254 // Add space for digit before the decimal point and the '\0' character.
255 const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
256 DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
257 char decimal_rep[kDecimalRepCapacity];
258 #ifndef NDEBUG
259 // Problem: there is an assert in StringBuilder::AddSubstring() that
260 // will pass this buffer to strlen(), and this buffer is not generally
261 // null-terminated.
262 memset(decimal_rep, 0, sizeof(decimal_rep));
263 #endif
264 int decimal_rep_length;
265
266 if (requested_digits == -1) {
267 DoubleToAscii(value, SHORTEST, 0,
268 decimal_rep, kDecimalRepCapacity,
269 &sign, &decimal_rep_length, &decimal_point);
270 } else {
271 DoubleToAscii(value, PRECISION, requested_digits + 1,
272 decimal_rep, kDecimalRepCapacity,
273 &sign, &decimal_rep_length, &decimal_point);
274 DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1);
275
276 for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
277 decimal_rep[i] = '0';
278 }
279 decimal_rep_length = requested_digits + 1;
280 }
281
282 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
283 if (sign && (value != 0.0 || !unique_zero)) {
284 result_builder->AddCharacter('-');
285 }
286
287 int exponent = decimal_point - 1;
288 CreateExponentialRepresentation(decimal_rep,
289 decimal_rep_length,
290 exponent,
291 result_builder);
292 return true;
293 }
294
295
ToPrecision(double value,int precision,StringBuilder * result_builder) const296 bool DoubleToStringConverter::ToPrecision(double value,
297 int precision,
298 StringBuilder* result_builder) const {
299 if (Double(value).IsSpecial()) {
300 return HandleSpecialValues(value, result_builder);
301 }
302
303 if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
304 return false;
305 }
306
307 // Find a sufficiently precise decimal representation of n.
308 int decimal_point;
309 bool sign;
310 // Add one for the terminating null character.
311 const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
312 char decimal_rep[kDecimalRepCapacity];
313 int decimal_rep_length;
314
315 DoubleToAscii(value, PRECISION, precision,
316 decimal_rep, kDecimalRepCapacity,
317 &sign, &decimal_rep_length, &decimal_point);
318 DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision);
319
320 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
321 if (sign && (value != 0.0 || !unique_zero)) {
322 result_builder->AddCharacter('-');
323 }
324
325 // The exponent if we print the number as x.xxeyyy. That is with the
326 // decimal point after the first digit.
327 int exponent = decimal_point - 1;
328
329 int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
330 bool as_exponential =
331 (-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
332 (decimal_point - precision + extra_zero >
333 max_trailing_padding_zeroes_in_precision_mode_);
334 if ((flags_ & NO_TRAILING_ZERO) != 0) {
335 // Truncate trailing zeros that occur after the decimal point (if exponential,
336 // that is everything after the first digit).
337 int stop = as_exponential ? 1 : std::max(1, decimal_point);
338 while (decimal_rep_length > stop && decimal_rep[decimal_rep_length - 1] == '0') {
339 --decimal_rep_length;
340 }
341 // Clamp precision to avoid the code below re-adding the zeros.
342 precision = std::min(precision, decimal_rep_length);
343 }
344 if (as_exponential) {
345 // Fill buffer to contain 'precision' digits.
346 // Usually the buffer is already at the correct length, but 'DoubleToAscii'
347 // is allowed to return less characters.
348 for (int i = decimal_rep_length; i < precision; ++i) {
349 decimal_rep[i] = '0';
350 }
351
352 CreateExponentialRepresentation(decimal_rep,
353 precision,
354 exponent,
355 result_builder);
356 } else {
357 CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
358 (std::max)(0, precision - decimal_point),
359 result_builder);
360 }
361 return true;
362 }
363
364
DtoaToBignumDtoaMode(DoubleToStringConverter::DtoaMode dtoa_mode)365 static BignumDtoaMode DtoaToBignumDtoaMode(
366 DoubleToStringConverter::DtoaMode dtoa_mode) {
367 switch (dtoa_mode) {
368 case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
369 case DoubleToStringConverter::SHORTEST_SINGLE:
370 return BIGNUM_DTOA_SHORTEST_SINGLE;
371 case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
372 case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
373 default:
374 DOUBLE_CONVERSION_UNREACHABLE();
375 }
376 }
377
378
DoubleToAscii(double v,DtoaMode mode,int requested_digits,char * buffer,int buffer_length,bool * sign,int * length,int * point)379 void DoubleToStringConverter::DoubleToAscii(double v,
380 DtoaMode mode,
381 int requested_digits,
382 char* buffer,
383 int buffer_length,
384 bool* sign,
385 int* length,
386 int* point) {
387 Vector<char> vector(buffer, buffer_length);
388 DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
389 DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
390
391 if (Double(v).Sign() < 0) {
392 *sign = true;
393 v = -v;
394 } else {
395 *sign = false;
396 }
397
398 if (mode == PRECISION && requested_digits == 0) {
399 vector[0] = '\0';
400 *length = 0;
401 return;
402 }
403
404 if (v == 0) {
405 vector[0] = '0';
406 vector[1] = '\0';
407 *length = 1;
408 *point = 1;
409 return;
410 }
411
412 bool fast_worked;
413 switch (mode) {
414 case SHORTEST:
415 fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
416 break;
417 case SHORTEST_SINGLE:
418 fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0,
419 vector, length, point);
420 break;
421 case FIXED:
422 fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
423 break;
424 case PRECISION:
425 fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
426 vector, length, point);
427 break;
428 default:
429 fast_worked = false;
430 DOUBLE_CONVERSION_UNREACHABLE();
431 }
432 if (fast_worked) return;
433
434 // If the fast dtoa didn't succeed use the slower bignum version.
435 BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
436 BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
437 vector[*length] = '\0';
438 }
439
440 } // namespace double_conversion
441