| /* |
| * Copyright 1999-2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "DoubleSupport.hpp" |
| |
| |
| |
| #include <clocale> |
| #include <cmath> |
| |
| |
| |
| #include "DOMStringHelper.hpp" |
| #include "XalanUnicode.hpp" |
| |
| |
| |
| XALAN_CPP_NAMESPACE_BEGIN |
| |
| |
| |
| DoubleSupport::NumberUnion DoubleSupport::s_NaN; |
| |
| const DoubleSupport::NumberUnion DoubleSupport::s_positiveInfinity = { HUGE_VAL }; |
| |
| const DoubleSupport::NumberUnion DoubleSupport::s_negativeInfinity = { -HUGE_VAL }; |
| |
| const DoubleSupport::NumberUnion DoubleSupport::s_positiveZero = { 0.0 }; |
| |
| const DoubleSupport::NumberUnion DoubleSupport::s_negativeZero = { -s_positiveZero.d }; |
| |
| |
| |
| void |
| DoubleSupport::initialize() |
| { |
| // We initialize this at here because some |
| // platforms have had issues with signals |
| // if we call sqrt(-2.01) during static |
| // initialization. |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| s_NaN.d = std::sqrt(-2.01); |
| #else |
| s_NaN.d = sqrt(-2.01); |
| #endif |
| } |
| |
| |
| |
| void |
| DoubleSupport::terminate() |
| { |
| s_NaN.d = 0.0L; |
| } |
| |
| |
| |
| bool |
| DoubleSupport::equal( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true || isNaN(theRHS) == true) |
| { |
| return false; |
| } |
| else |
| { |
| return theLHS == theRHS; |
| } |
| } |
| |
| |
| |
| bool |
| DoubleSupport::lessThan( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true || isNaN(theRHS) == true) |
| { |
| return false; |
| } |
| else |
| { |
| return theLHS < theRHS; |
| } |
| } |
| |
| |
| |
| bool |
| DoubleSupport::lessThanOrEqual( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true || isNaN(theRHS) == true) |
| { |
| return false; |
| } |
| else |
| { |
| return theLHS <= theRHS; |
| } |
| } |
| |
| |
| |
| bool |
| DoubleSupport::greaterThan( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true || isNaN(theRHS) == true) |
| { |
| return false; |
| } |
| else |
| { |
| return theLHS > theRHS; |
| } |
| } |
| |
| |
| |
| bool |
| DoubleSupport::greaterThanOrEqual( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true || isNaN(theRHS) == true) |
| { |
| return false; |
| } |
| else |
| { |
| return theLHS >= theRHS; |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::add( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true) |
| { |
| return theLHS; |
| } |
| else if (isNaN(theRHS) == true) |
| { |
| return theRHS; |
| } |
| else |
| { |
| return theLHS + theRHS; |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::subtract( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true) |
| { |
| return theLHS; |
| } |
| else if (isNaN(theRHS) == true) |
| { |
| return theRHS; |
| } |
| else |
| { |
| return theLHS - theRHS; |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::multiply( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true) |
| { |
| return theLHS; |
| } |
| else if (isNaN(theRHS) == true) |
| { |
| return theRHS; |
| } |
| else |
| { |
| return theLHS * theRHS; |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::divide( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true) |
| { |
| return theLHS; |
| } |
| else if (isNaN(theRHS) == true) |
| { |
| return theRHS; |
| } |
| else if (theRHS != 0.0L) |
| { |
| return theLHS / theRHS; |
| } |
| else if (theLHS == 0.0L) |
| { |
| // This is NaN... |
| return DoubleSupport::getNaN(); |
| } |
| else if (theLHS > 0.0L && isPositiveZero(theRHS) == true) |
| { |
| // This is positive infinity... |
| return DoubleSupport::getPositiveInfinity(); |
| } |
| else |
| { |
| // This is negative infinity... |
| return DoubleSupport::getNegativeInfinity(); |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::modulus( |
| double theLHS, |
| double theRHS) |
| { |
| if (isNaN(theLHS) == true) |
| { |
| return theLHS; |
| } |
| else if (isNaN(theRHS) == true) |
| { |
| return theRHS; |
| } |
| else if (theRHS == 0.0) |
| { |
| return getNaN(); |
| } |
| else if (long(theLHS) == theLHS && long(theRHS) == theRHS) |
| { |
| return long(theLHS) % long(theRHS); |
| } |
| else |
| { |
| double theDummy; |
| |
| double theResult = divide(theLHS, theRHS); |
| |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| return std::modf(theResult, &theDummy) * theRHS; |
| #else |
| return modf(theResult, &theDummy) * theRHS; |
| #endif |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::negative(double theDouble) |
| { |
| if (isNaN(theDouble) == true) |
| { |
| return getNaN(); |
| } |
| else |
| { |
| return -theDouble; |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::toDouble(const XalanDOMString& theString) |
| { |
| return toDouble(c_wstr(theString)); |
| } |
| |
| |
| |
| inline void |
| consumeWhitespace(const XalanDOMChar*& theString) |
| { |
| while(*theString != 0 && |
| isXMLWhitespace(*theString)) |
| { |
| ++theString; |
| } |
| } |
| |
| |
| |
| inline void |
| consumeWhitespace( |
| const XalanDOMChar*& theString, |
| XalanDOMString::size_type& theLength) |
| { |
| while(*theString != 0 && |
| isXMLWhitespace(*theString)) |
| { |
| ++theString; |
| --theLength; |
| } |
| } |
| |
| |
| |
| inline static void |
| consumeNumbers(const XalanDOMChar*& theString) |
| { |
| while(*theString && |
| *theString >= XalanUnicode::charDigit_0 && |
| *theString <= XalanUnicode::charDigit_9) |
| { |
| ++theString; |
| } |
| } |
| |
| |
| |
| static bool |
| doValidate( |
| const XalanDOMChar* theString, |
| bool& fGotDecimalPoint) |
| { |
| assert(theString != 0); |
| |
| bool fError = false; |
| bool fGotDigit = false; |
| bool fGotMinus = false; |
| bool fGotWhitespace = false; |
| |
| const XalanDOMChar* theCurrent = theString; |
| |
| // trim any whitespace |
| consumeWhitespace(theCurrent); |
| |
| while(*theCurrent != 0 && fError == false) |
| { |
| switch(*theCurrent) |
| { |
| case XalanUnicode::charFullStop: |
| if (fGotDecimalPoint == true || // can't have more than one... |
| fGotWhitespace == true) // can't have one after whitespace... |
| { |
| fError = true; |
| } |
| else |
| { |
| fGotDecimalPoint = true; |
| |
| ++theCurrent; |
| } |
| break; |
| |
| case XalanUnicode::charHyphenMinus: |
| if (fGotDecimalPoint == true || |
| fGotMinus == true || |
| fGotDigit == true || |
| fGotWhitespace == true) |
| { |
| // Error -- more than one, or in bad position. |
| fError = true; |
| } |
| else |
| { |
| fGotMinus = true; |
| |
| ++theCurrent; |
| } |
| break; |
| |
| case XalanUnicode::charDigit_0: |
| case XalanUnicode::charDigit_1: |
| case XalanUnicode::charDigit_2: |
| case XalanUnicode::charDigit_3: |
| case XalanUnicode::charDigit_4: |
| case XalanUnicode::charDigit_5: |
| case XalanUnicode::charDigit_6: |
| case XalanUnicode::charDigit_7: |
| case XalanUnicode::charDigit_8: |
| case XalanUnicode::charDigit_9: |
| if (fGotWhitespace == true) |
| { |
| fError = true; |
| } |
| else |
| { |
| fGotDigit = true; |
| |
| consumeNumbers(theCurrent); |
| } |
| break; |
| |
| case XalanUnicode::charSpace: |
| case XalanUnicode::charCR: |
| case XalanUnicode::charHTab: |
| case XalanUnicode::charLF: |
| if (fGotWhitespace == true) |
| { |
| fError = true; |
| } |
| else |
| { |
| fGotWhitespace = true; |
| |
| consumeWhitespace(theCurrent); |
| } |
| break; |
| |
| default: |
| fError = true; |
| break; |
| } |
| } |
| |
| // If there was no error, check to see that we got |
| // at least one digit. Otherwise, return false if |
| // there was an error. |
| return fError == false ? fGotDigit : false; |
| } |
| |
| |
| |
| static bool |
| doValidate(const XalanDOMChar* theString) |
| { |
| bool fDummy = false; |
| |
| return doValidate(theString, fDummy); |
| } |
| |
| |
| |
| #if defined(XALAN_NON_ASCII_PLATFORM) |
| void |
| translateWideString( |
| const XalanDOMChar* theWideString, |
| char* theNarrowString, |
| XalanDOMString::size_type theStringLength, |
| char theDecimalPointCharacter) |
| { |
| for(XalanDOMString::size_type i = 0; i < theStringLength; ++i) |
| { |
| switch(theWideString[i]) |
| { |
| case XalanUnicode::charHyphenMinus: |
| theNarrowString[i] = '-'; |
| break; |
| |
| case XalanUnicode::charFullStop: |
| theNarrowString[i] = theDecimalPointCharacter; |
| break; |
| |
| case XalanUnicode::charDigit_0: |
| theNarrowString[i] = '0'; |
| break; |
| |
| case XalanUnicode::charDigit_1: |
| theNarrowString[i] = '1'; |
| break; |
| |
| case XalanUnicode::charDigit_2: |
| theNarrowString[i] = '2'; |
| break; |
| |
| case XalanUnicode::charDigit_3: |
| theNarrowString[i] = '3'; |
| break; |
| |
| case XalanUnicode::charDigit_4: |
| theNarrowString[i] = '4'; |
| break; |
| |
| case XalanUnicode::charDigit_5: |
| theNarrowString[i] = '5'; |
| break; |
| |
| case XalanUnicode::charDigit_6: |
| theNarrowString[i] = '6'; |
| break; |
| |
| case XalanUnicode::charDigit_7: |
| theNarrowString[i] = '7'; |
| break; |
| |
| case XalanUnicode::charDigit_8: |
| theNarrowString[i] = '8'; |
| break; |
| |
| case XalanUnicode::charDigit_9: |
| theNarrowString[i] = '9'; |
| break; |
| |
| default: |
| theNarrowString[i] = char(0); |
| break; |
| } |
| } |
| |
| theNarrowString[theStringLength] = char(0); |
| } |
| #endif |
| |
| |
| |
| inline double |
| convertHelper( |
| const XalanDOMChar* theString, |
| bool fGotDecimalPoint) |
| { |
| // This is a big hack. If the length of the |
| // string is less than n characters, we'll convert |
| // it as a long and coerce that to a double. This |
| // is _much_ cheaper... |
| const XalanDOMString::size_type theLongHackThreshold = 10; |
| |
| XalanDOMString::size_type theLength = length(theString); |
| |
| if (fGotDecimalPoint == false && theLength < theLongHackThreshold) |
| { |
| return double(WideStringToLong(theString)); |
| } |
| else |
| { |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| const char theDecimalPointChar = std::localeconv()->decimal_point[0]; |
| #else |
| const char theDecimalPointChar = localeconv()->decimal_point[0]; |
| #endif |
| |
| // trim any whitespace |
| consumeWhitespace(theString, theLength); |
| |
| // Use a stack-based buffer, when possible... |
| const XalanDOMString::size_type theBufferSize = 200u; |
| |
| if (theLength < theBufferSize) |
| { |
| char theBuffer[theBufferSize]; |
| |
| #if defined(XALAN_NON_ASCII_PLATFORM) |
| translateWideString(theString, theBuffer, theLength, theDecimalPointChar); |
| #else |
| for(XalanDOMString::size_type i = 0; i < theLength; ++i) |
| { |
| if (theString[i] == XalanUnicode::charFullStop) |
| { |
| theBuffer[i] = theDecimalPointChar; |
| } |
| else |
| { |
| theBuffer[i] = char(theString[i]); |
| } |
| } |
| |
| theBuffer[theLength] = '\0'; |
| #endif |
| |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| return std::atof(theBuffer); |
| #else |
| return atof(theBuffer); |
| #endif |
| } |
| else |
| { |
| CharVectorType theVector; |
| |
| #if !defined(XALAN_NON_ASCII_PLATFORM) |
| theVector.reserve(theLength + 1); |
| |
| CopyWideStringToVector(theString, theVector); |
| #else |
| theVector.resize(theLength + 1, CharVectorType::value_type(0)); |
| |
| translateWideString(theString, &*theVector.begin(), theLength, theDecimalPointChar); |
| #endif |
| |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| return std::atof(&*theVector.begin()); |
| #else |
| return atof(&*theVector.begin()); |
| #endif |
| } |
| } |
| } |
| |
| |
| |
| double |
| doConvert(const XalanDOMChar* theString) |
| { |
| assert(theString != 0); |
| assert(*theString != 0); |
| |
| bool fGotDecimalPoint = false; |
| |
| if (doValidate(theString, fGotDecimalPoint) == false) |
| { |
| return DoubleSupport::getNaN(); |
| } |
| else |
| { |
| return convertHelper(theString, fGotDecimalPoint); |
| } |
| } |
| |
| |
| |
| double |
| DoubleSupport::toDouble(const XalanDOMChar* theString) |
| { |
| if (theString == 0 || |
| *theString == 0) |
| { |
| return getNaN(); |
| } |
| else |
| { |
| return doConvert(theString); |
| } |
| } |
| |
| |
| |
| bool |
| DoubleSupport::isValid(const XalanDOMString& theString) |
| { |
| return isValid(c_wstr(theString)); |
| } |
| |
| |
| |
| bool |
| DoubleSupport::isValid(const XalanDOMChar* theString) |
| { |
| return doValidate(theString); |
| } |
| |
| |
| |
| double |
| DoubleSupport::round(double theValue) |
| { |
| if (isNaN(theValue)) |
| { |
| return getNaN(); |
| } |
| else if (isPositiveInfinity(theValue)) |
| { |
| return getPositiveInfinity(); |
| } |
| if (isNegativeInfinity(theValue)) |
| { |
| return getNegativeInfinity(); |
| } |
| else if (theValue == 0) |
| { |
| return 0.0; |
| } |
| else if (theValue > 0) |
| { |
| return long(theValue + 0.5); |
| } |
| else |
| { |
| // Negative numbers are a special case. Any time we |
| // have -0.5 as the fractional part, we have to |
| // round up (toward 0), rather than down. |
| double intPart = 0; |
| |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| const double fracPart = std::modf(theValue, &intPart); |
| #else |
| const double fracPart = modf(theValue, &intPart); |
| #endif |
| |
| if (fracPart == -0.5) |
| { |
| // special case -- we have have to round toward 0... |
| return long(theValue + 0.5); |
| } |
| else |
| { |
| return long(theValue - 0.5); |
| } |
| } |
| } |
| |
| |
| |
| XALAN_CPP_NAMESPACE_END |