blob: 653af521801915212a36827d1d733a684b3fa1e2 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
#include "DoubleSupport.hpp"
#include <cmath>
#include "DOMStringHelper.hpp"
#include "XalanUnicode.hpp"
// To circumvent an OS/390 problem
#if !defined(OS390)
#define XALAN_POSITIVE_INFINITY HUGE_VAL
#else
static const union
{
unsigned char c[8];
double d;
} theHugeVal = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } };
#define XALAN_POSITIVE_INFINITY (theHugeVal.d)
#endif
const double DoubleSupport::s_NaN = sqrt(-2.01);
const double DoubleSupport::s_positiveInfinity = XALAN_POSITIVE_INFINITY;
const double DoubleSupport::s_negativeInfinity = -DoubleSupport::s_positiveInfinity;
const double DoubleSupport::s_positiveZero = 0.0;
#if !defined(_AIX)
const double DoubleSupport::s_negativeZero = -DoubleSupport::s_positiveZero;
#else
// Some compiler are overly aggressive and think that there is no such thing as -0,
// so we have to get it in a very sneaky way.
double theDummy;
const double DoubleSupport::s_negativeZero = modf(-7.0, &theDummy);
#endif
typedef DoubleSupport::DWORDPointerType DWORDPointerType;
typedef DoubleSupport::UnalignedDWORDPointerType UnalignedDWORDPointerType;
const DWORDPointerType DoubleSupport::s_NaNFirstDWORD =
#if defined(XALAN_OLD_STYLE_CASTS)
(DWORDPointerType)&s_NaN;
#else
reinterpret_cast<DWORDPointerType>(&s_NaN);
#endif
const UnalignedDWORDPointerType DoubleSupport::s_NaNSecondDWORD =
s_NaNFirstDWORD + 1;
const DWORDPointerType DoubleSupport::s_positiveZeroFirstDWORD =
#if defined(XALAN_OLD_STYLE_CASTS)
(DWORDPointerType)&s_positiveZero;
#else
reinterpret_cast<DWORDPointerType>(&s_positiveZero);
#endif
const UnalignedDWORDPointerType DoubleSupport::s_positiveZeroSecondDWORD = s_positiveZeroFirstDWORD + 1;
const DWORDPointerType DoubleSupport::s_negativeZeroFirstDWORD =
#if defined(XALAN_OLD_STYLE_CASTS)
(DWORDPointerType)&s_negativeZero;
#else
reinterpret_cast<DWORDPointerType>(&s_negativeZero);
#endif
const UnalignedDWORDPointerType DoubleSupport::s_negativeZeroSecondDWORD = s_negativeZeroFirstDWORD + 1;
bool
DoubleSupport::equal(
double theLHS,
double theRHS)
{
if (isNaN(theLHS) == true || isNaN(theRHS) == true)
{
return false;
}
else
{
return theLHS == theRHS;
}
}
bool
DoubleSupport::notEqual(
double theLHS,
double theRHS)
{
return !equal(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);
return modf(theResult, &theDummy) * theRHS;
}
}
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)
{
for(XalanDOMString::size_type i = 0; i < theStringLength; ++i)
{
switch(theWideString[i])
{
case XalanUnicode::charHyphenMinus:
theNarrowString[i] = '-';
break;
case XalanUnicode::charFullStop:
theNarrowString[i] = '.';
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
{
// 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);
#else
for(XalanDOMString::size_type i = 0; i < theLength; ++i)
{
theBuffer[i] = char(theString[i]);
}
theBuffer[theLength] = '\0';
#endif
return atof(theBuffer);
}
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);
#endif
return atof(&*theVector.begin());
}
}
}
double
doConvert(const XalanDOMChar* theString)
{
assert(theString != 0);
assert(*theString != 0);
#if 0
bool fError = false;
bool fGotDecimalPoint = 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 (fError == true || fGotDigit == false)
{
return DoubleSupport::getNaN();
}
else
{
#if defined(XALAN_FULL_WCHAR_SUPPORT) && defined(XALAN_USE_WCHAR_SUPPORT)
XalanDOMChar* theDummy;
return wcstod(theString, &theDummy);
#else
return convertHelper(theString, fGotDecimalPoint);
#endif
}
#else
bool fGotDecimalPoint = false;
if (doValidate(theString, fGotDecimalPoint) == false)
{
return DoubleSupport::getNaN();
}
else
{
#if defined(XALAN_FULL_WCHAR_SUPPORT) && defined(XALAN_USE_WCHAR_SUPPORT)
XalanDOMChar* theDummy;
return wcstod(theString, &theDummy);
#else
return convertHelper(theString, fGotDecimalPoint);
#endif
}
#endif
}
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);
}
#if 0
// This version is disabled because it turns out that
// an unsigned long is not large enough to accumulate
// all values (duh!). Perhaps on 64-bit platforms, we
// can use this code, as it's much faster.
void
accumulateNumbers(
const XalanDOMChar*& theString,
double& theResult,
bool fAfterDecimal)
{
if (fAfterDecimal == false)
{
assert(theResult == 0.0);
// accumulate as an integer, to avoid
// rounding issues. It's also much
// faster...
double temp = 0;
while(*theString &&
*theString >= XalanUnicode::charDigit_0 &&
*theString <= XalanUnicode::charDigit_9)
{
temp *= 10;
temp += char(*theString) - XalanUnicode::charDigit_0;
++theString;
}
theResult = temp;
}
else
{
// Accumulate a divisor, so we can divide at the end.
double theDivisor = 1;
// accumulate as an integer, to avoid
// rounding issues. It's also much
// faster...
unsigned long temp = 0;
while(*theString &&
*theString >= XalanUnicode::charDigit_0 &&
*theString <= XalanUnicode::charDigit_9)
{
theDivisor *= 10;
temp *= 10;
temp += char(*theString) - XalanUnicode::charDigit_0;
++theString;
}
if (temp > 0 && theDivisor > 1)
{
const double theFactionalPart =
double(temp) / double(theDivisor);
theResult += theFactionalPart;
}
}
}
double
doConvert(const XalanDOMChar* theString)
{
assert(theString != 0);
assert(*theString != 0);
double theResult = 0.0;
bool fError = false;
bool fGotDecimalPoint = 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;
accumulateNumbers(theCurrent, theResult, fGotDecimalPoint);
}
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 (fError == true || fGotDigit == false)
{
return DoubleSupport::getNaN();
}
else
{
return fGotMinus == true ? -theResult : theResult;
}
}
#endif
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;
const double fracPart = modf(theValue, &intPart);
if (fracPart == -0.5)
{
// special case -- we have have to round toward 0...
return long(theValue + 0.5);
}
else
{
return long(theValue - 0.5);
}
}
}