txDouble.cpp (4694B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 <math.h> 7 8 #include <algorithm> 9 10 #include "mozilla/FloatingPoint.h" 11 #include "nsString.h" 12 #include "txCore.h" 13 #include "txXMLUtils.h" 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 public: 29 txStringToDouble() : mState(eWhitestart), mSign(ePositive) {} 30 31 void Parse(const nsAString& aSource) { 32 if (mState == eIllegal) { 33 return; 34 } 35 uint32_t i = 0; 36 char16_t c; 37 auto len = aSource.Length(); 38 for (; i < len; ++i) { 39 c = aSource[i]; 40 switch (mState) { 41 case eWhitestart: 42 if (c == '-') { 43 mState = eDecimal; 44 mSign = eNegative; 45 } else if (c >= '0' && c <= '9') { 46 mState = eDecimal; 47 mBuffer.Append((char)c); 48 } else if (c == '.') { 49 mState = eMantissa; 50 mBuffer.Append((char)c); 51 } else if (!XMLUtils::isWhitespace(c)) { 52 mState = eIllegal; 53 return; 54 } 55 break; 56 case eDecimal: 57 if (c >= '0' && c <= '9') { 58 mBuffer.Append((char)c); 59 } else if (c == '.') { 60 mState = eMantissa; 61 mBuffer.Append((char)c); 62 } else if (XMLUtils::isWhitespace(c)) { 63 mState = eWhiteend; 64 } else { 65 mState = eIllegal; 66 return; 67 } 68 break; 69 case eMantissa: 70 if (c >= '0' && c <= '9') { 71 mBuffer.Append((char)c); 72 } else if (XMLUtils::isWhitespace(c)) { 73 mState = eWhiteend; 74 } else { 75 mState = eIllegal; 76 return; 77 } 78 break; 79 case eWhiteend: 80 if (!XMLUtils::isWhitespace(c)) { 81 mState = eIllegal; 82 return; 83 } 84 break; 85 default: 86 break; 87 } 88 } 89 } 90 91 double getDouble() { 92 if (mState == eIllegal || mBuffer.IsEmpty() || 93 (mBuffer.Length() == 1 && mBuffer[0] == '.')) { 94 return mozilla::UnspecifiedNaN<double>(); 95 } 96 return static_cast<double>(mSign) * PR_strtod(mBuffer.get(), nullptr); 97 } 98 99 private: 100 nsAutoCString mBuffer; 101 enum { eWhitestart, eDecimal, eMantissa, eWhiteend, eIllegal } mState; 102 enum { eNegative = -1, ePositive = 1 } mSign; 103 }; 104 105 double txDouble::toDouble(const nsAString& aSrc) { 106 txStringToDouble sink; 107 sink.Parse(aSrc); 108 return sink.getDouble(); 109 } 110 111 /* 112 * Converts the value of the given double to a String, and places 113 * The result into the destination String. 114 * @return the given dest string 115 */ 116 void txDouble::toString(double aValue, nsAString& aDest) { 117 // check for special cases 118 119 if (std::isnan(aValue)) { 120 aDest.AppendLiteral("NaN"); 121 return; 122 } 123 if (std::isinf(aValue)) { 124 if (aValue < 0) aDest.Append(char16_t('-')); 125 aDest.AppendLiteral("Infinity"); 126 return; 127 } 128 129 // Mantissa length is 17, so this is plenty 130 const int buflen = 20; 131 char buf[buflen]; 132 133 int intDigits, sign; 134 char* endp; 135 PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1); 136 137 // compute length 138 int32_t length = endp - buf; 139 if (length > intDigits) { 140 // decimal point needed 141 ++length; 142 if (intDigits < 1) { 143 // leading zeros, -intDigits + 1 144 length += 1 - intDigits; 145 } 146 } else { 147 // trailing zeros, total length given by intDigits 148 length = intDigits; 149 } 150 if (aValue < 0) ++length; 151 // grow the string 152 uint32_t oldlength = aDest.Length(); 153 if (!aDest.SetLength(oldlength + length, mozilla::fallible)) 154 return; // out of memory 155 auto dest = aDest.BeginWriting(); 156 std::advance(dest, oldlength); 157 if (aValue < 0) { 158 *dest = '-'; 159 ++dest; 160 } 161 int i; 162 // leading zeros 163 if (intDigits < 1) { 164 *dest = '0'; 165 ++dest; 166 *dest = '.'; 167 ++dest; 168 for (i = 0; i > intDigits; --i) { 169 *dest = '0'; 170 ++dest; 171 } 172 } 173 // mantissa 174 int firstlen = std::min<size_t>(intDigits, endp - buf); 175 for (i = 0; i < firstlen; i++) { 176 *dest = buf[i]; 177 ++dest; 178 } 179 if (i < endp - buf) { 180 if (i > 0) { 181 *dest = '.'; 182 ++dest; 183 } 184 for (; i < endp - buf; i++) { 185 *dest = buf[i]; 186 ++dest; 187 } 188 } 189 // trailing zeros 190 for (; i < intDigits; i++) { 191 *dest = '0'; 192 ++dest; 193 } 194 }