1// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/double_conversion.h"
6
7#include "third_party/double-conversion/src/double-conversion.h"
8
9#include "vm/exceptions.h"
10#include "vm/globals.h"
11#include "vm/object.h"
12
13namespace dart {
14
15char const DoubleToStringConstants::kExponentChar = 'e';
16const char* const DoubleToStringConstants::kInfinitySymbol = "Infinity";
17const char* const DoubleToStringConstants::kNaNSymbol = "NaN";
18
19void DoubleToCString(double d, char* buffer, int buffer_size) {
20 static const int kDecimalLow = -6;
21 static const int kDecimalHigh = 21;
22
23 // The output contains the sign, at most kDecimalHigh - 1 digits,
24 // the decimal point followed by a 0 plus the \0.
25 ASSERT(buffer_size >= 1 + (kDecimalHigh - 1) + 1 + 1 + 1);
26 // Or it contains the sign, a 0, the decimal point, kDecimalLow '0's,
27 // 17 digits (the precision needed for doubles), plus the \0.
28 ASSERT(buffer_size >= 1 + 1 + 1 + kDecimalLow + 17 + 1);
29 // Alternatively it contains a sign, at most 17 digits (precision needed for
30 // any double), the decimal point, the exponent character, the exponent's
31 // sign, at most three exponent digits, plus the \0.
32 ASSERT(buffer_size >= 1 + 17 + 1 + 1 + 1 + 3 + 1);
33
34 static const int kConversionFlags =
35 double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
36 double_conversion::DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT |
37 double_conversion::DoubleToStringConverter::
38 EMIT_TRAILING_ZERO_AFTER_POINT;
39
40 const double_conversion::DoubleToStringConverter converter(
41 kConversionFlags, DoubleToStringConstants::kInfinitySymbol,
42 DoubleToStringConstants::kNaNSymbol,
43 DoubleToStringConstants::kExponentChar, kDecimalLow, kDecimalHigh, 0,
44 0); // Last two values are ignored in shortest mode.
45
46 double_conversion::StringBuilder builder(buffer, buffer_size);
47 bool status = converter.ToShortest(d, &builder);
48 ASSERT(status);
49 char* result = builder.Finalize();
50 ASSERT(result == buffer);
51}
52
53StringPtr DoubleToStringAsFixed(double d, int fraction_digits) {
54 static const int kMinFractionDigits = 0;
55 static const int kMaxFractionDigits = 20;
56 static const int kMaxDigitsBeforePoint = 20;
57 // The boundaries are exclusive.
58 static const double kLowerBoundary = -1e21;
59 static const double kUpperBoundary = 1e21;
60 // TODO(floitsch): remove the UNIQUE_ZERO flag when the test is updated.
61 static const int kConversionFlags =
62 double_conversion::DoubleToStringConverter::NO_FLAGS;
63 const int kBufferSize = 128;
64
65 USE(kMaxDigitsBeforePoint);
66 USE(kMaxFractionDigits);
67 USE(kLowerBoundary);
68 USE(kUpperBoundary);
69 USE(kMinFractionDigits);
70 USE(kMaxFractionDigits);
71 // The output contains the sign, at most kMaxDigitsBeforePoint digits,
72 // the decimal point followed by at most fraction_digits digits plus the \0.
73 ASSERT(kBufferSize >= 1 + kMaxDigitsBeforePoint + 1 + kMaxFractionDigits + 1);
74
75 ASSERT(kLowerBoundary < d && d < kUpperBoundary);
76
77 ASSERT(kMinFractionDigits <= fraction_digits &&
78 fraction_digits <= kMaxFractionDigits);
79
80 const double_conversion::DoubleToStringConverter converter(
81 kConversionFlags, DoubleToStringConstants::kInfinitySymbol,
82 DoubleToStringConstants::kNaNSymbol,
83 DoubleToStringConstants::kExponentChar, 0, 0, 0,
84 0); // Last four values are ignored in fixed mode.
85
86 char* buffer = Thread::Current()->zone()->Alloc<char>(kBufferSize);
87 buffer[kBufferSize - 1] = '\0';
88 double_conversion::StringBuilder builder(buffer, kBufferSize);
89 bool status = converter.ToFixed(d, fraction_digits, &builder);
90 ASSERT(status);
91 return String::New(builder.Finalize());
92}
93
94StringPtr DoubleToStringAsExponential(double d, int fraction_digits) {
95 static const int kMinFractionDigits = -1; // -1 represents shortest mode.
96 static const int kMaxFractionDigits = 20;
97 static const int kConversionFlags =
98 double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
99 const int kBufferSize = 128;
100
101 USE(kMinFractionDigits);
102 USE(kMaxFractionDigits);
103 // The output contains the sign, at most 1 digits, the decimal point followed
104 // by at most kMaxFractionDigits digits, the exponent-character, the
105 // exponent-sign and three exponent digits plus \0.
106 ASSERT(kBufferSize >= 1 + 1 + kMaxFractionDigits + 1 + 1 + 3 + 1);
107
108 ASSERT(kMinFractionDigits <= fraction_digits &&
109 fraction_digits <= kMaxFractionDigits);
110
111 const double_conversion::DoubleToStringConverter converter(
112 kConversionFlags, DoubleToStringConstants::kInfinitySymbol,
113 DoubleToStringConstants::kNaNSymbol,
114 DoubleToStringConstants::kExponentChar, 0, 0, 0,
115 0); // Last four values are ignored in exponential mode.
116
117 char* buffer = Thread::Current()->zone()->Alloc<char>(kBufferSize);
118 buffer[kBufferSize - 1] = '\0';
119 double_conversion::StringBuilder builder(buffer, kBufferSize);
120 bool status = converter.ToExponential(d, fraction_digits, &builder);
121 ASSERT(status);
122 return String::New(builder.Finalize());
123}
124
125StringPtr DoubleToStringAsPrecision(double d, int precision) {
126 static const int kMinPrecisionDigits = 1;
127 static const int kMaxPrecisionDigits = 21;
128 static const int kMaxLeadingPaddingZeroes = 6;
129 static const int kMaxTrailingPaddingZeroes = 0;
130 static const int kConversionFlags =
131 double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
132 const int kBufferSize = 128;
133
134 USE(kMinPrecisionDigits);
135 USE(kMaxPrecisionDigits);
136 // The output contains the sign, a potential leading 0, the decimal point,
137 // at most kMax{Leading|Trailing} padding zeroes, precision digits,
138 // the exponent-character, the exponent-sign, three exponent digits
139 // plus the \0.
140 // Note that padding and exponent are exclusive. We still add them up.
141 ASSERT(kBufferSize >= 1 + 1 + 1 + kMaxLeadingPaddingZeroes +
142 kMaxTrailingPaddingZeroes + kMaxPrecisionDigits +
143 1 + 1 + 3 + 1);
144
145 ASSERT(kMinPrecisionDigits <= precision && precision <= kMaxPrecisionDigits);
146
147 const double_conversion::DoubleToStringConverter converter(
148 kConversionFlags, DoubleToStringConstants::kInfinitySymbol,
149 DoubleToStringConstants::kNaNSymbol,
150 DoubleToStringConstants::kExponentChar, 0,
151 0, // Ignored in precision mode.
152 kMaxLeadingPaddingZeroes, kMaxTrailingPaddingZeroes);
153
154 char* buffer = Thread::Current()->zone()->Alloc<char>(kBufferSize);
155 buffer[kBufferSize - 1] = '\0';
156 double_conversion::StringBuilder builder(buffer, kBufferSize);
157 bool status = converter.ToPrecision(d, precision, &builder);
158 ASSERT(status);
159 return String::New(builder.Finalize());
160}
161
162bool CStringToDouble(const char* str, intptr_t length, double* result) {
163 if (length == 0) {
164 return false;
165 }
166
167 double_conversion::StringToDoubleConverter converter(
168 double_conversion::StringToDoubleConverter::NO_FLAGS, 0.0, 0.0,
169 DoubleToStringConstants::kInfinitySymbol,
170 DoubleToStringConstants::kNaNSymbol);
171
172 int parsed_count = 0;
173 *result =
174 converter.StringToDouble(str, static_cast<int>(length), &parsed_count);
175 return (parsed_count == length);
176}
177
178} // namespace dart
179