blob: 514b2d15217f3a168ce7f47f60e73c11e40e116d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <climits>
#include <limits>
#include "DOMStringHelper.hpp"
#include "XalanUnicode.hpp"
namespace XALAN_CPP_NAMESPACE {
const DoubleSupport::NumberUnion DoubleSupport::s_NaN =
{
std::numeric_limits<double>::quiet_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()
{
// There seems to be problems with various standard libraries, so
// this is disabled for now. We need to revisit this when we
// update our autoconf/automake system to detect the right value
// for NaN at configuration time.
// XALAN_STATIC_ASSERT(std::numeric_limits<double>::has_quiet_NaN);
}
void
DoubleSupport::terminate()
{
}
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 getNaN();
}
else if (theLHS > 0.0L && isPositiveZero(theRHS) == true)
{
// This is positive infinity...
return getPositiveInfinity();
}
else
{
// This is negative infinity...
return 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 std::modf(theResult, &theDummy) * theRHS;
}
}
double
DoubleSupport::negative(double theDouble)
{
if (isNaN(theDouble) == true)
{
return getNaN();
}
else
{
return -theDouble;
}
}
double
DoubleSupport::abs(double theDouble)
{
if (isNaN(theDouble) == true)
{
return getNaN();
}
else
{
using std::fabs;
return fabs(theDouble);
}
}
double
DoubleSupport::toDouble(
const XalanDOMString& theString,
MemoryManager& theManager)
{
return toDouble(theString.c_str(), theManager);
}
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)
static 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,
MemoryManager& theManager)
{
// 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
{
using std::localeconv;
using std::atof;
const char theDecimalPointChar =
localeconv()->decimal_point[0];
// 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
return atof(theBuffer);
}
else
{
CharVectorType theVector(theManager);
#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
return atof(&*theVector.begin());
}
}
}
inline double
doConvert(
const XalanDOMChar* theString,
MemoryManager& theManager)
{
assert(theString != 0);
assert(*theString != 0);
bool fGotDecimalPoint = false;
if (doValidate(theString, fGotDecimalPoint) == false)
{
return DoubleSupport::getNaN();
}
else
{
return convertHelper(
theString,
fGotDecimalPoint,
theManager);
}
}
double
DoubleSupport::toDouble(
const XalanDOMChar* theString,
MemoryManager& theManager)
{
if (theString == 0 ||
*theString == 0)
{
return getNaN();
}
else
{
return doConvert(theString, theManager);
}
}
bool
DoubleSupport::isValid(const XalanDOMString& theString)
{
return isValid(theString.c_str());
}
bool
DoubleSupport::isValid(const XalanDOMChar* theString)
{
return doValidate(theString);
}
inline double
modfRound(double theValue)
{
double intPart = 0;
std::modf(theValue + 0.5, &intPart);
return intPart;
}
double
DoubleSupport::round(double theValue)
{
if (isNaN(theValue))
{
return getNaN();
}
else if (isPositiveInfinity(theValue))
{
return getPositiveInfinity();
}
else if (isNegativeInfinity(theValue))
{
return getNegativeInfinity();
}
else if (theValue == 0)
{
return 0.0;
}
else if (theValue > 0)
{
// If the value is less than the maximum value for
// a long, this is the fastest way to do it.
if (theValue < LONG_MAX)
{
return long(theValue + 0.5);
}
else
{
return modfRound(theValue);
}
}
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 =
std::modf(theValue, &intPart);
const double theAdjustedValue =
fracPart == -0.5 ? theValue + 0.5 : theValue - 0.5;
// If the value is greater than the minimum value for
// a long, this is the fastest way to do it.
if (theAdjustedValue > LONG_MIN)
{
return long(theAdjustedValue);
}
else
{
return modfRound(theAdjustedValue);
}
}
}
}