Line data Source code
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 0 : class txStringToDouble
28 : {
29 : public:
30 : typedef char16_t input_type;
31 : typedef char16_t value_type;
32 0 : txStringToDouble(): mState(eWhitestart), mSign(ePositive) {}
33 :
34 : void
35 0 : write(const input_type* aSource, uint32_t aSourceLength)
36 : {
37 0 : if (mState == eIllegal) {
38 0 : return;
39 : }
40 0 : uint32_t i = 0;
41 : char16_t c;
42 0 : for ( ; i < aSourceLength; ++i) {
43 0 : c = aSource[i];
44 0 : switch (mState) {
45 : case eWhitestart:
46 0 : if (c == '-') {
47 0 : mState = eDecimal;
48 0 : mSign = eNegative;
49 : }
50 0 : else if (c >= '0' && c <= '9') {
51 0 : mState = eDecimal;
52 0 : mBuffer.Append((char)c);
53 : }
54 0 : else if (c == '.') {
55 0 : mState = eMantissa;
56 0 : mBuffer.Append((char)c);
57 : }
58 0 : else if (!XMLUtils::isWhitespace(c)) {
59 0 : mState = eIllegal;
60 0 : return;
61 : }
62 0 : break;
63 : case eDecimal:
64 0 : if (c >= '0' && c <= '9') {
65 0 : mBuffer.Append((char)c);
66 : }
67 0 : else if (c == '.') {
68 0 : mState = eMantissa;
69 0 : mBuffer.Append((char)c);
70 : }
71 0 : else if (XMLUtils::isWhitespace(c)) {
72 0 : mState = eWhiteend;
73 : }
74 : else {
75 0 : mState = eIllegal;
76 0 : return;
77 : }
78 0 : break;
79 : case eMantissa:
80 0 : if (c >= '0' && c <= '9') {
81 0 : mBuffer.Append((char)c);
82 : }
83 0 : else if (XMLUtils::isWhitespace(c)) {
84 0 : mState = eWhiteend;
85 : }
86 : else {
87 0 : mState = eIllegal;
88 0 : return;
89 : }
90 0 : break;
91 : case eWhiteend:
92 0 : if (!XMLUtils::isWhitespace(c)) {
93 0 : mState = eIllegal;
94 0 : return;
95 : }
96 0 : break;
97 : default:
98 0 : break;
99 : }
100 : }
101 : }
102 :
103 : double
104 0 : getDouble()
105 : {
106 0 : if (mState == eIllegal || mBuffer.IsEmpty() ||
107 0 : (mBuffer.Length() == 1 && mBuffer[0] == '.')) {
108 0 : return mozilla::UnspecifiedNaN<double>();
109 : }
110 0 : 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 0 : double txDouble::toDouble(const nsAString& aSrc)
128 : {
129 0 : txStringToDouble sink;
130 0 : nsAString::const_iterator fromBegin, fromEnd;
131 0 : copy_string(aSrc.BeginReading(fromBegin), aSrc.EndReading(fromEnd), sink);
132 0 : 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 0 : void txDouble::toString(double aValue, nsAString& aDest)
141 : {
142 :
143 : // check for special cases
144 :
145 0 : if (mozilla::IsNaN(aValue)) {
146 0 : aDest.AppendLiteral("NaN");
147 0 : return;
148 : }
149 0 : if (mozilla::IsInfinite(aValue)) {
150 0 : if (aValue < 0)
151 0 : aDest.Append(char16_t('-'));
152 0 : aDest.AppendLiteral("Infinity");
153 0 : return;
154 : }
155 :
156 : // Mantissa length is 17, so this is plenty
157 0 : const int buflen = 20;
158 : char buf[buflen];
159 :
160 : int intDigits, sign;
161 : char* endp;
162 0 : PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1);
163 :
164 : // compute length
165 0 : int32_t length = endp - buf;
166 0 : if (length > intDigits) {
167 : // decimal point needed
168 0 : ++length;
169 0 : if (intDigits < 1) {
170 : // leading zeros, -intDigits + 1
171 0 : length += 1 - intDigits;
172 : }
173 : }
174 : else {
175 : // trailing zeros, total length given by intDigits
176 0 : length = intDigits;
177 : }
178 0 : if (aValue < 0)
179 0 : ++length;
180 : // grow the string
181 0 : uint32_t oldlength = aDest.Length();
182 0 : if (!aDest.SetLength(oldlength + length, mozilla::fallible))
183 0 : return; // out of memory
184 0 : nsAString::iterator dest;
185 0 : aDest.BeginWriting(dest).advance(int32_t(oldlength));
186 0 : if (aValue < 0) {
187 0 : *dest = '-'; ++dest;
188 : }
189 : int i;
190 : // leading zeros
191 0 : if (intDigits < 1) {
192 0 : *dest = '0'; ++dest;
193 0 : *dest = '.'; ++dest;
194 0 : for (i = 0; i > intDigits; --i) {
195 0 : *dest = '0'; ++dest;
196 : }
197 : }
198 : // mantissa
199 0 : int firstlen = std::min<size_t>(intDigits, endp - buf);
200 0 : for (i = 0; i < firstlen; i++) {
201 0 : *dest = buf[i]; ++dest;
202 : }
203 0 : if (i < endp - buf) {
204 0 : if (i > 0) {
205 0 : *dest = '.'; ++dest;
206 : }
207 0 : for (; i < endp - buf; i++) {
208 0 : *dest = buf[i]; ++dest;
209 : }
210 : }
211 : // trailing zeros
212 0 : for (; i < intDigits; i++) {
213 0 : *dest = '0'; ++dest;
214 : }
215 : }
|