|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "mozilla/FloatingPoint.h" |
|
7 |
|
8 #include "nsString.h" |
|
9 #include "txCore.h" |
|
10 #include "txXMLUtils.h" |
|
11 #include <math.h> |
|
12 #include <stdlib.h> |
|
13 #include <algorithm> |
|
14 #ifdef WIN32 |
|
15 #include <float.h> |
|
16 #endif |
|
17 #include "prdtoa.h" |
|
18 |
|
19 /* |
|
20 * Utility class for doubles |
|
21 */ |
|
22 |
|
23 /* |
|
24 * Converts the given String to a double, if the String value does not |
|
25 * represent a double, NaN will be returned |
|
26 */ |
|
27 class txStringToDouble |
|
28 { |
|
29 public: |
|
30 typedef char16_t input_type; |
|
31 typedef char16_t value_type; |
|
32 txStringToDouble(): mState(eWhitestart), mSign(ePositive) {} |
|
33 |
|
34 void |
|
35 write(const input_type* aSource, uint32_t aSourceLength) |
|
36 { |
|
37 if (mState == eIllegal) { |
|
38 return; |
|
39 } |
|
40 uint32_t i = 0; |
|
41 char16_t c; |
|
42 for ( ; i < aSourceLength; ++i) { |
|
43 c = aSource[i]; |
|
44 switch (mState) { |
|
45 case eWhitestart: |
|
46 if (c == '-') { |
|
47 mState = eDecimal; |
|
48 mSign = eNegative; |
|
49 } |
|
50 else if (c >= '0' && c <= '9') { |
|
51 mState = eDecimal; |
|
52 mBuffer.Append((char)c); |
|
53 } |
|
54 else if (c == '.') { |
|
55 mState = eMantissa; |
|
56 mBuffer.Append((char)c); |
|
57 } |
|
58 else if (!XMLUtils::isWhitespace(c)) { |
|
59 mState = eIllegal; |
|
60 return; |
|
61 } |
|
62 break; |
|
63 case eDecimal: |
|
64 if (c >= '0' && c <= '9') { |
|
65 mBuffer.Append((char)c); |
|
66 } |
|
67 else if (c == '.') { |
|
68 mState = eMantissa; |
|
69 mBuffer.Append((char)c); |
|
70 } |
|
71 else if (XMLUtils::isWhitespace(c)) { |
|
72 mState = eWhiteend; |
|
73 } |
|
74 else { |
|
75 mState = eIllegal; |
|
76 return; |
|
77 } |
|
78 break; |
|
79 case eMantissa: |
|
80 if (c >= '0' && c <= '9') { |
|
81 mBuffer.Append((char)c); |
|
82 } |
|
83 else if (XMLUtils::isWhitespace(c)) { |
|
84 mState = eWhiteend; |
|
85 } |
|
86 else { |
|
87 mState = eIllegal; |
|
88 return; |
|
89 } |
|
90 break; |
|
91 case eWhiteend: |
|
92 if (!XMLUtils::isWhitespace(c)) { |
|
93 mState = eIllegal; |
|
94 return; |
|
95 } |
|
96 break; |
|
97 default: |
|
98 break; |
|
99 } |
|
100 } |
|
101 } |
|
102 |
|
103 double |
|
104 getDouble() |
|
105 { |
|
106 if (mState == eIllegal || mBuffer.IsEmpty() || |
|
107 (mBuffer.Length() == 1 && mBuffer[0] == '.')) { |
|
108 return mozilla::UnspecifiedNaN<double>(); |
|
109 } |
|
110 return mSign*PR_strtod(mBuffer.get(), 0); |
|
111 } |
|
112 private: |
|
113 nsAutoCString mBuffer; |
|
114 enum { |
|
115 eWhitestart, |
|
116 eDecimal, |
|
117 eMantissa, |
|
118 eWhiteend, |
|
119 eIllegal |
|
120 } mState; |
|
121 enum { |
|
122 eNegative = -1, |
|
123 ePositive = 1 |
|
124 } mSign; |
|
125 }; |
|
126 |
|
127 double txDouble::toDouble(const nsAString& aSrc) |
|
128 { |
|
129 txStringToDouble sink; |
|
130 nsAString::const_iterator fromBegin, fromEnd; |
|
131 copy_string(aSrc.BeginReading(fromBegin), aSrc.EndReading(fromEnd), sink); |
|
132 return sink.getDouble(); |
|
133 } |
|
134 |
|
135 /* |
|
136 * Converts the value of the given double to a String, and places |
|
137 * The result into the destination String. |
|
138 * @return the given dest string |
|
139 */ |
|
140 void txDouble::toString(double aValue, nsAString& aDest) |
|
141 { |
|
142 |
|
143 // check for special cases |
|
144 |
|
145 if (mozilla::IsNaN(aValue)) { |
|
146 aDest.AppendLiteral("NaN"); |
|
147 return; |
|
148 } |
|
149 if (mozilla::IsInfinite(aValue)) { |
|
150 if (aValue < 0) |
|
151 aDest.Append(char16_t('-')); |
|
152 aDest.AppendLiteral("Infinity"); |
|
153 return; |
|
154 } |
|
155 |
|
156 // Mantissa length is 17, so this is plenty |
|
157 const int buflen = 20; |
|
158 char buf[buflen]; |
|
159 |
|
160 int intDigits, sign; |
|
161 char* endp; |
|
162 PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1); |
|
163 |
|
164 // compute length |
|
165 int32_t length = endp - buf; |
|
166 if (length > intDigits) { |
|
167 // decimal point needed |
|
168 ++length; |
|
169 if (intDigits < 1) { |
|
170 // leading zeros, -intDigits + 1 |
|
171 length += 1 - intDigits; |
|
172 } |
|
173 } |
|
174 else { |
|
175 // trailing zeros, total length given by intDigits |
|
176 length = intDigits; |
|
177 } |
|
178 if (aValue < 0) |
|
179 ++length; |
|
180 // grow the string |
|
181 uint32_t oldlength = aDest.Length(); |
|
182 if (!aDest.SetLength(oldlength + length, mozilla::fallible_t())) |
|
183 return; // out of memory |
|
184 nsAString::iterator dest; |
|
185 aDest.BeginWriting(dest).advance(int32_t(oldlength)); |
|
186 if (aValue < 0) { |
|
187 *dest = '-'; ++dest; |
|
188 } |
|
189 int i; |
|
190 // leading zeros |
|
191 if (intDigits < 1) { |
|
192 *dest = '0'; ++dest; |
|
193 *dest = '.'; ++dest; |
|
194 for (i = 0; i > intDigits; --i) { |
|
195 *dest = '0'; ++dest; |
|
196 } |
|
197 } |
|
198 // mantissa |
|
199 int firstlen = std::min<size_t>(intDigits, endp - buf); |
|
200 for (i = 0; i < firstlen; i++) { |
|
201 *dest = buf[i]; ++dest; |
|
202 } |
|
203 if (i < endp - buf) { |
|
204 if (i > 0) { |
|
205 *dest = '.'; ++dest; |
|
206 } |
|
207 for (; i < endp - buf; i++) { |
|
208 *dest = buf[i]; ++dest; |
|
209 } |
|
210 } |
|
211 // trailing zeros |
|
212 for (; i < intDigits; i++) { |
|
213 *dest = '0'; ++dest; |
|
214 } |
|
215 } |