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