Back to index

lightning-sunbird  0.9+nobinonly
Double.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is TransforMiiX XSLT processor code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * The MITRE Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsString.h"
00039 #include "txCore.h"
00040 #include "XMLUtils.h"
00041 #include <math.h>
00042 #include <stdlib.h>
00043 #ifdef WIN32
00044 #include <float.h>
00045 #endif
00046 #include "prdtoa.h"
00047 
00048 /*
00049  * Utility class for doubles
00050  */
00051 
00052 //A trick to handle IEEE floating point exceptions on FreeBSD - E.D.
00053 #ifdef __FreeBSD__
00054 #include <ieeefp.h>
00055 #ifdef __alpha__
00056 fp_except_t allmask = FP_X_INV|FP_X_OFL|FP_X_UFL|FP_X_DZ|FP_X_IMP;
00057 #else
00058 fp_except_t allmask = FP_X_INV|FP_X_OFL|FP_X_UFL|FP_X_DZ|FP_X_IMP|FP_X_DNML;
00059 #endif
00060 fp_except_t oldmask = fpsetmask(~allmask);
00061 #endif
00062 
00074 #if defined(__arm) || defined(__arm32__) || defined(_arm26__) || defined(__arm__)
00075 #define CPU_IS_ARM
00076 #endif
00077 
00078 #if (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || __GNUC__ > 2
00079 
00084 typedef union txdpun {
00085     PRFloat64 d;
00086     struct {
00087 #if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM)
00088         PRUint32 lo, hi;
00089 #else
00090         PRUint32 hi, lo;
00091 #endif
00092     } s;
00093 } txdpun;
00094 
00095 #define TX_DOUBLE_HI32(x) (__extension__ ({ txdpun u; u.d = (x); u.s.hi; }))
00096 #define TX_DOUBLE_LO32(x) (__extension__ ({ txdpun u; u.d = (x); u.s.lo; }))
00097 
00098 #else // __GNUC__
00099 
00100 /* We don't know of any non-gcc compilers that perform alias optimization,
00101  * so this code should work.
00102  */
00103 
00104 #if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM)
00105 #define TX_DOUBLE_HI32(x)        (((PRUint32 *)&(x))[1])
00106 #define TX_DOUBLE_LO32(x)        (((PRUint32 *)&(x))[0])
00107 #else
00108 #define TX_DOUBLE_HI32(x)        (((PRUint32 *)&(x))[0])
00109 #define TX_DOUBLE_LO32(x)        (((PRUint32 *)&(x))[1])
00110 #endif
00111 
00112 #endif // __GNUC__
00113 
00114 #define TX_DOUBLE_HI32_SIGNBIT   0x80000000
00115 #define TX_DOUBLE_HI32_EXPMASK   0x7ff00000
00116 #define TX_DOUBLE_HI32_MANTMASK  0x000fffff
00117 
00118 //-- Initialize Double related constants
00119 #ifdef IS_BIG_ENDIAN
00120 const PRUint32 nanMask[2] =    {TX_DOUBLE_HI32_EXPMASK | TX_DOUBLE_HI32_MANTMASK,
00121                                 0xffffffff};
00122 const PRUint32 infMask[2] =    {TX_DOUBLE_HI32_EXPMASK, 0};
00123 const PRUint32 negInfMask[2] = {TX_DOUBLE_HI32_EXPMASK | TX_DOUBLE_HI32_SIGNBIT, 0};
00124 #else
00125 const PRUint32 nanMask[2] =    {0xffffffff,
00126                                 TX_DOUBLE_HI32_EXPMASK | TX_DOUBLE_HI32_MANTMASK};
00127 const PRUint32 infMask[2] =    {0, TX_DOUBLE_HI32_EXPMASK};
00128 const PRUint32 negInfMask[2] = {0, TX_DOUBLE_HI32_EXPMASK | TX_DOUBLE_HI32_SIGNBIT};
00129 #endif
00130 
00131 const double Double::NaN = *((double*)nanMask);
00132 const double Double::POSITIVE_INFINITY = *((double*)infMask);
00133 const double Double::NEGATIVE_INFINITY = *((double*)negInfMask);
00134 
00135 /*
00136  * Determines whether the given double represents positive or negative
00137  * inifinity
00138  */
00139 MBool Double::isInfinite(double aDbl)
00140 {
00141     return ((TX_DOUBLE_HI32(aDbl) & ~TX_DOUBLE_HI32_SIGNBIT) == TX_DOUBLE_HI32_EXPMASK &&
00142             !TX_DOUBLE_LO32(aDbl));
00143 }
00144 
00145 /*
00146  * Determines whether the given double is NaN
00147  */
00148 MBool Double::isNaN(double aDbl)
00149 {
00150     return ((TX_DOUBLE_HI32(aDbl) & TX_DOUBLE_HI32_EXPMASK) == TX_DOUBLE_HI32_EXPMASK &&
00151             (TX_DOUBLE_LO32(aDbl) || (TX_DOUBLE_HI32(aDbl) & TX_DOUBLE_HI32_MANTMASK)));
00152 }
00153 
00154 /*
00155  * Determines whether the given double is negative
00156  */
00157 MBool Double::isNeg(double aDbl)
00158 {
00159     return (TX_DOUBLE_HI32(aDbl) & TX_DOUBLE_HI32_SIGNBIT) != 0;
00160 }
00161 
00162 /*
00163  * Converts the given String to a double, if the String value does not
00164  * represent a double, NaN will be returned
00165  */
00166 class txStringToDouble
00167 {
00168 public:
00169     typedef PRUnichar input_type;
00170     typedef PRUnichar value_type;
00171     txStringToDouble(): mState(eWhitestart), mSign(ePositive) {}
00172 
00173     PRUint32
00174     write(const input_type* aSource, PRUint32 aSourceLength)
00175     {
00176         if (mState == eIllegal) {
00177             return aSourceLength;
00178         }
00179         PRUint32 i = 0;
00180         PRUnichar c;
00181         for ( ; i < aSourceLength; ++i) {
00182             c = aSource[i];
00183             switch (mState) {
00184                 case eWhitestart:
00185                     if (c == '-') {
00186                         mState = eDecimal;
00187                         mSign = eNegative;
00188                     }
00189                     else if (c >= '0' && c <= '9') {
00190                         mState = eDecimal;
00191                         mBuffer.Append((char)c);
00192                     }
00193                     else if (c == '.') {
00194                         mState = eMantissa;
00195                         mBuffer.Append((char)c);
00196                     }
00197                     else if (!XMLUtils::isWhitespace(c)) {
00198                         mState = eIllegal;
00199                         return aSourceLength;
00200                     }
00201                     break;
00202                 case eDecimal:
00203                     if (c >= '0' && c <= '9') {
00204                         mBuffer.Append((char)c);
00205                     }
00206                     else if (c == '.') {
00207                         mState = eMantissa;
00208                         mBuffer.Append((char)c);
00209                     }
00210                     else if (XMLUtils::isWhitespace(c)) {
00211                         mState = eWhiteend;
00212                     }
00213                     else {
00214                         mState = eIllegal;
00215                         return aSourceLength;
00216                     }
00217                     break;
00218                 case eMantissa:
00219                     if (c >= '0' && c <= '9') {
00220                         mBuffer.Append((char)c);
00221                     }
00222                     else if (XMLUtils::isWhitespace(c)) {
00223                         mState = eWhiteend;
00224                     }
00225                     else {
00226                         mState = eIllegal;
00227                         return aSourceLength;
00228                     }
00229                     break;
00230                 case eWhiteend:
00231                     if (!XMLUtils::isWhitespace(c)) {
00232                         mState = eIllegal;
00233                         return aSourceLength;
00234                     }
00235                     break;
00236                 default:
00237                     break;
00238             }
00239         }
00240         return aSourceLength;
00241     }
00242 
00243     double
00244     getDouble()
00245     {
00246         if (mState == eIllegal || mBuffer.IsEmpty() ||
00247             (mBuffer.Length() == 1 && mBuffer[0] == '.')) {
00248             return Double::NaN;
00249         }
00250         return mSign*PR_strtod(mBuffer.get(), 0);
00251     }
00252 private:
00253     nsCAutoString mBuffer;
00254     enum {
00255         eWhitestart,
00256         eDecimal,
00257         eMantissa,
00258         eWhiteend,
00259         eIllegal
00260     } mState;
00261     enum {
00262         eNegative = -1,
00263         ePositive = 1
00264     } mSign;
00265 };
00266 
00267 double Double::toDouble(const nsAString& aSrc)
00268 {
00269     txStringToDouble sink;
00270     nsAString::const_iterator fromBegin, fromEnd;
00271     copy_string(aSrc.BeginReading(fromBegin), aSrc.EndReading(fromEnd), sink);
00272     return sink.getDouble();
00273 }
00274 
00275 /*
00276  * Converts the value of the given double to a String, and places
00277  * The result into the destination String.
00278  * @return the given dest string
00279  */
00280 void Double::toString(double aValue, nsAString& aDest)
00281 {
00282 
00283     // check for special cases
00284 
00285     if (isNaN(aValue)) {
00286         aDest.AppendLiteral("NaN");
00287         return;
00288     }
00289     if (isInfinite(aValue)) {
00290         if (aValue < 0)
00291             aDest.Append(PRUnichar('-'));
00292         aDest.AppendLiteral("Infinity");
00293         return;
00294     }
00295 
00296     // Mantissa length is 17, so this is plenty
00297     const int buflen = 20;
00298     char buf[buflen];
00299 
00300     PRIntn intDigits, sign;
00301     char* endp;
00302     PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1);
00303 
00304     // compute length
00305     PRInt32 length = endp - buf;
00306     if (length > intDigits) {
00307         // decimal point needed
00308         ++length;
00309         if (intDigits < 1) {
00310             // leading zeros, -intDigits + 1
00311             length += 1 - intDigits;
00312         }
00313     }
00314     else {
00315         // trailing zeros, total length given by intDigits
00316         length = intDigits;
00317     }
00318     if (aValue < 0)
00319         ++length;
00320     // grow the string
00321     PRUint32 oldlength = aDest.Length();
00322     if (!EnsureStringLength(aDest, oldlength + length))
00323         return; // out of memory
00324     nsAString::iterator dest;
00325     aDest.BeginWriting(dest).advance(PRInt32(oldlength));
00326     if (aValue < 0) {
00327         *dest = '-'; ++dest;
00328     }
00329     int i;
00330     // leading zeros
00331     if (intDigits < 1) {
00332         *dest = '0'; ++dest;
00333         *dest = '.'; ++dest;
00334         for (i = 0; i > intDigits; --i) {
00335             *dest = '0'; ++dest;
00336         }
00337     }
00338     // mantissa
00339     int firstlen = PR_MIN(intDigits, endp - buf);
00340     for (i = 0; i < firstlen; i++) {
00341         *dest = buf[i]; ++dest;
00342     }
00343     if (i < endp - buf) {
00344         if (i > 0) {
00345             *dest = '.'; ++dest;
00346         }
00347         for (; i < endp - buf; i++) {
00348             *dest = buf[i]; ++dest;
00349         }
00350     }
00351     // trailing zeros
00352     for (; i < intDigits; i++) {
00353         *dest = '0'; ++dest;
00354     }
00355 }