• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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