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_ == NULL) 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_ == NULL) 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 if (Double(value).IsSpecial()) {
209 return HandleSpecialValues(value, result_builder);
210 }
211
212 if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
213
214 // Find a sufficiently precise decimal representation of n.
215 int decimal_point;
216 bool sign;
217 // Add space for the '\0' byte.
218 const int kDecimalRepCapacity =
219 kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
220 char decimal_rep[kDecimalRepCapacity];
221 int decimal_rep_length;
222 DoubleToAscii(value, FIXED, requested_digits,
223 decimal_rep, kDecimalRepCapacity,
224 &sign, &decimal_rep_length, &decimal_point);
225
226 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
227 if (sign && (value != 0.0 || !unique_zero)) {
228 result_builder->AddCharacter('-');
229 }
230
231 CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
232 requested_digits, result_builder);
233 return true;
234 }
235
236
ToExponential(double value,int requested_digits,StringBuilder * result_builder) const237 bool DoubleToStringConverter::ToExponential(
238 double value,
239 int requested_digits,
240 StringBuilder* result_builder) const {
241 if (Double(value).IsSpecial()) {
242 return HandleSpecialValues(value, result_builder);
243 }
244
245 if (requested_digits < -1) return false;
246 if (requested_digits > kMaxExponentialDigits) return false;
247
248 int decimal_point;
249 bool sign;
250 // Add space for digit before the decimal point and the '\0' character.
251 const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
252 DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
253 char decimal_rep[kDecimalRepCapacity];
254 #ifndef NDEBUG
255 // Problem: there is an assert in StringBuilder::AddSubstring() that
256 // will pass this buffer to strlen(), and this buffer is not generally
257 // null-terminated.
258 memset(decimal_rep, 0, sizeof(decimal_rep));
259 #endif
260 int decimal_rep_length;
261
262 if (requested_digits == -1) {
263 DoubleToAscii(value, SHORTEST, 0,
264 decimal_rep, kDecimalRepCapacity,
265 &sign, &decimal_rep_length, &decimal_point);
266 } else {
267 DoubleToAscii(value, PRECISION, requested_digits + 1,
268 decimal_rep, kDecimalRepCapacity,
269 &sign, &decimal_rep_length, &decimal_point);
270 DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1);
271
272 for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
273 decimal_rep[i] = '0';
274 }
275 decimal_rep_length = requested_digits + 1;
276 }
277
278 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
279 if (sign && (value != 0.0 || !unique_zero)) {
280 result_builder->AddCharacter('-');
281 }
282
283 int exponent = decimal_point - 1;
284 CreateExponentialRepresentation(decimal_rep,
285 decimal_rep_length,
286 exponent,
287 result_builder);
288 return true;
289 }
290
291
ToPrecision(double value,int precision,StringBuilder * result_builder) const292 bool DoubleToStringConverter::ToPrecision(double value,
293 int precision,
294 StringBuilder* result_builder) const {
295 if (Double(value).IsSpecial()) {
296 return HandleSpecialValues(value, result_builder);
297 }
298
299 if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
300 return false;
301 }
302
303 // Find a sufficiently precise decimal representation of n.
304 int decimal_point;
305 bool sign;
306 // Add one for the terminating null character.
307 const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
308 char decimal_rep[kDecimalRepCapacity];
309 int decimal_rep_length;
310
311 DoubleToAscii(value, PRECISION, precision,
312 decimal_rep, kDecimalRepCapacity,
313 &sign, &decimal_rep_length, &decimal_point);
314 DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision);
315
316 bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
317 if (sign && (value != 0.0 || !unique_zero)) {
318 result_builder->AddCharacter('-');
319 }
320
321 // The exponent if we print the number as x.xxeyyy. That is with the
322 // decimal point after the first digit.
323 int exponent = decimal_point - 1;
324
325 int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
326 bool as_exponential =
327 (-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
328 (decimal_point - precision + extra_zero >
329 max_trailing_padding_zeroes_in_precision_mode_);
330 if ((flags_ & NO_TRAILING_ZERO) != 0) {
331 // Truncate trailing zeros that occur after the decimal point (if exponential,
332 // that is everything after the first digit).
333 int stop = as_exponential ? 1 : std::max(1, decimal_point);
334 while (decimal_rep_length > stop && decimal_rep[decimal_rep_length - 1] == '0') {
335 --decimal_rep_length;
336 }
337 // Clamp precision to avoid the code below re-adding the zeros.
338 precision = std::min(precision, decimal_rep_length);
339 }
340 if (as_exponential) {
341 // Fill buffer to contain 'precision' digits.
342 // Usually the buffer is already at the correct length, but 'DoubleToAscii'
343 // is allowed to return less characters.
344 for (int i = decimal_rep_length; i < precision; ++i) {
345 decimal_rep[i] = '0';
346 }
347
348 CreateExponentialRepresentation(decimal_rep,
349 precision,
350 exponent,
351 result_builder);
352 } else {
353 CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
354 (std::max)(0, precision - decimal_point),
355 result_builder);
356 }
357 return true;
358 }
359
360
DtoaToBignumDtoaMode(DoubleToStringConverter::DtoaMode dtoa_mode)361 static BignumDtoaMode DtoaToBignumDtoaMode(
362 DoubleToStringConverter::DtoaMode dtoa_mode) {
363 switch (dtoa_mode) {
364 case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
365 case DoubleToStringConverter::SHORTEST_SINGLE:
366 return BIGNUM_DTOA_SHORTEST_SINGLE;
367 case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
368 case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
369 default:
370 DOUBLE_CONVERSION_UNREACHABLE();
371 }
372 }
373
374
DoubleToAscii(double v,DtoaMode mode,int requested_digits,char * buffer,int buffer_length,bool * sign,int * length,int * point)375 void DoubleToStringConverter::DoubleToAscii(double v,
376 DtoaMode mode,
377 int requested_digits,
378 char* buffer,
379 int buffer_length,
380 bool* sign,
381 int* length,
382 int* point) {
383 Vector<char> vector(buffer, buffer_length);
384 DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
385 DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
386
387 if (Double(v).Sign() < 0) {
388 *sign = true;
389 v = -v;
390 } else {
391 *sign = false;
392 }
393
394 if (mode == PRECISION && requested_digits == 0) {
395 vector[0] = '\0';
396 *length = 0;
397 return;
398 }
399
400 if (v == 0) {
401 vector[0] = '0';
402 vector[1] = '\0';
403 *length = 1;
404 *point = 1;
405 return;
406 }
407
408 bool fast_worked;
409 switch (mode) {
410 case SHORTEST:
411 fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
412 break;
413 case SHORTEST_SINGLE:
414 fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0,
415 vector, length, point);
416 break;
417 case FIXED:
418 fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
419 break;
420 case PRECISION:
421 fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
422 vector, length, point);
423 break;
424 default:
425 fast_worked = false;
426 DOUBLE_CONVERSION_UNREACHABLE();
427 }
428 if (fast_worked) return;
429
430 // If the fast dtoa didn't succeed use the slower bignum version.
431 BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
432 BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
433 vector[*length] = '\0';
434 }
435
436 } // namespace double_conversion
437