txDouble.cpp 5.7 KB

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