diff --git a/src/gmio_core/internal/fast_atof.h b/src/gmio_core/internal/fast_atof.h index cf4063e..9180b35 100644 --- a/src/gmio_core/internal/fast_atof.h +++ b/src/gmio_core/internal/fast_atof.h @@ -1,364 +1,361 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine" and the "irrXML" project. -// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h +/* Copyright (C) 2002-2012 Nikolaus Gebhardt + * This file is part of the "Irrlicht Engine" and the "irrXML" project. + * For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h + */ -#ifndef __FAST_ATOF_H_INCLUDED__ -#define __FAST_ATOF_H_INCLUDED__ +#include "../global.h" -#include "irrMath.h" -#include "irrString.h" +#include +#include -namespace irr +GMIO_INLINE gmio_bool_t is_local_decimal_point(char in) { -namespace core -{ - //! Selection of characters which count as decimal point in fast_atof - // TODO: This should probably also be used in irr::core::string, but the float-to-string code - // used there has to be rewritten first. - IRRLICHT_API extern irr::core::stringc LOCALE_DECIMAL_POINTS; + /*! Selection of characters which count as decimal point in fast_atof + * TODO: This should probably also be used in irr::core::string, but + * the float-to-string code used there has to be rewritten first. + */ + static const char LOCALE_DECIMAL_POINTS[] = { '.' }; -// we write [17] here instead of [] to work around a swig bug + if (in == LOCALE_DECIMAL_POINTS[0]) + return GMIO_TRUE; + return GMIO_FALSE; +} + +/* we write [17] here instead of [] to work around a swig bug */ const float fast_atof_table[17] = { - 0.f, - 0.1f, - 0.01f, - 0.001f, - 0.0001f, - 0.00001f, - 0.000001f, - 0.0000001f, - 0.00000001f, - 0.000000001f, - 0.0000000001f, - 0.00000000001f, - 0.000000000001f, - 0.0000000000001f, - 0.00000000000001f, - 0.000000000000001f, - 0.0000000000000001f + 0.f, + 0.1f, + 0.01f, + 0.001f, + 0.0001f, + 0.00001f, + 0.000001f, + 0.0000001f, + 0.00000001f, + 0.000000001f, + 0.0000000001f, + 0.00000000001f, + 0.000000000001f, + 0.0000000000001f, + 0.00000000000001f, + 0.000000000000001f, + 0.0000000000000001f }; -//! Convert a simple string of base 10 digits into an unsigned 32 bit integer. -/** \param[in] in: The string of digits to convert. No leading chars are - allowed, only digits 0 to 9. Parsing stops at the first non-digit. - \param[out] out: (optional) If provided, it will be set to point at the - first character not used in the calculation. - \return The unsigned integer value of the digits. If the string specifies - too many digits to encode in an u32 then INT_MAX will be returned. -*/ -inline u32 strtoul10(const char* in, const char** out=0) +/*! Convert a simple string of base 10 digits into an unsigned 32 bit integer. + * + * \param[in] in: The string of digits to convert. No leading chars are + * allowed, only digits 0 to 9. Parsing stops at the first non-digit. + * \param[out] out: (optional) If provided, it will be set to point at the + * first character not used in the calculation. + * \return The unsigned integer value of the digits. If the string specifies + * too many digits to encode in an uint32_t then INT_MAX will be returned. + */ +GMIO_INLINE uint32_t strtoul10(const char* in, const char** out) { - if (!in) - { - if (out) - *out = in; - return 0; - } - - bool overflow=false; - u32 unsignedValue = 0; - while ( ( *in >= '0') && ( *in <= '9' )) - { - const u32 tmp = ( unsignedValue * 10 ) + ( *in - '0' ); - if (tmp= '0') && ( *in <= '9' )) + { + const uint32_t tmp = ( unsignedValue * 10 ) + ( *in - '0' ); + if (tmp (u32)INT_MAX) - { - if (negative) - return (s32)INT_MIN; - else - return (s32)INT_MAX; - } + if (!in) + { + if (out) + *out = in; + return 0; + } + if (negative || ('+' == *in)) + ++in; + unsignedValue = strtoul10(in,out); + if (unsignedValue > (uint32_t)INT_MAX) + { + if (negative) + return (int32_t)INT_MIN; else - { - if (negative) - return -((s32)unsignedValue); - else - return (s32)unsignedValue; - } -} - -//! Convert a hex-encoded character to an unsigned integer. -/** \param[in] in The digit to convert. Only digits 0 to 9 and chars A-F,a-f - will be considered. - \return The unsigned integer value of the digit. 0xffffffff if the input is - not hex -*/ -inline u32 ctoul16(char in) -{ - if (in >= '0' && in <= '9') - return in - '0'; - else if (in >= 'a' && in <= 'f') - return 10u + in - 'a'; - else if (in >= 'A' && in <= 'F') - return 10u + in - 'A'; + return (int32_t)INT_MAX; + } + else + { + if (negative) + return -((int32_t)unsignedValue); else - return 0xffffffff; + return (int32_t)unsignedValue; + } } -//! Convert a simple string of base 16 digits into an unsigned 32 bit integer. -/** \param[in] in: The string of digits to convert. No leading chars are - allowed, only digits 0 to 9 and chars A-F,a-f are allowed. Parsing stops - at the first illegal char. - \param[out] out: (optional) If provided, it will be set to point at the - first character not used in the calculation. - \return The unsigned integer value of the digits. If the string specifies - too many digits to encode in an u32 then INT_MAX will be returned. -*/ -inline u32 strtoul16(const char* in, const char** out=0) +/*! Convert a hex-encoded character to an unsigned integer. + * + * \param[in] in The digit to convert. Only digits 0 to 9 and chars A-F,a-f + * will be considered. + * \return The unsigned integer value of the digit. 0xffffffff if the input is + * not hex + */ +GMIO_INLINE uint32_t ctoul16(char in) { - if (!in) - { - if (out) - *out = in; - return 0; - } + if (in >= '0' && in <= '9') + return in - '0'; + else if (in >= 'a' && in <= 'f') + return 10u + in - 'a'; + else if (in >= 'A' && in <= 'F') + return 10u + in - 'A'; + else + return 0xffffffff; +} - bool overflow=false; - u32 unsignedValue = 0; - while (true) - { - u32 tmp = 0; - if ((*in >= '0') && (*in <= '9')) - tmp = (unsignedValue << 4u) + (*in - '0'); - else if ((*in >= 'A') && (*in <= 'F')) - tmp = (unsignedValue << 4u) + (*in - 'A') + 10; - else if ((*in >= 'a') && (*in <= 'f')) - tmp = (unsignedValue << 4u) + (*in - 'a') + 10; - else - break; - if (tmp= '0') && (*in <= '7')) - tmp = (unsignedValue << 3u) + (*in - '0'); - else - break; - if (tmp= '0') && ( *in <= '9' ) ) - { - // If it looks like we're going to overflow, bail out - // now and start using floating point. - if (intValue >= MAX_SAFE_U32_VALUE) - break; - - intValue = (intValue * 10) + (*in - '0'); - ++in; - } - - f32 floatValue = (f32)intValue; - - // If there are any digits left to parse, then we need to use - // floating point arithmetic from here. - while ( ( *in >= '0') && ( *in <= '9' ) ) - { - floatValue = (floatValue * 10.f) + (f32)(*in - '0'); - ++in; - if (floatValue > FLT_MAX) // Just give up. - break; - } - - if (out) - *out = in; - - return floatValue; -} - -//! Provides a fast function for converting a string into a float. -/** This is not guaranteed to be as accurate as atof(), but is - approximately 6 to 8 times as fast. - \param[in] in The string to convert. - \param[out] result The resultant float will be written here. - \return Pointer to the first character in the string that wasn't used - to create the float value. -*/ -inline const char* fast_atof_move(const char* in, f32& result) -{ - // Please run the regression test when making any modifications to this function. - - result = 0.f; - if (!in) - return 0; - - const bool negative = ('-' == *in); - if (negative || ('+'==*in)) - ++in; - - f32 value = strtof10(in, &in); - - if ( LOCALE_DECIMAL_POINTS.findFirst(*in) >= 0 ) - { - const char* afterDecimal = ++in; - const f32 decimal = strtof10(in, &afterDecimal); - value += decimal * fast_atof_table[afterDecimal - in]; - in = afterDecimal; - } - - if ('e' == *in || 'E' == *in) - { - ++in; - // Assume that the exponent is a whole number. - // strtol10() will deal with both + and - signs, - // but calculate as f32 to prevent overflow at FLT_MAX - value *= powf(10.f, (f32)strtol10(in, &in)); - } - - result = negative?-value:value; - return in; -} - -//! Convert a string to a floating point number -/** \param floatAsString The string to convert. - \param out Optional pointer to the first character in the string that - wasn't used to create the float value. - \result Float value parsed from the input string -*/ -inline float fast_atof(const char* floatAsString, const char** out=0) -{ - float ret; - if (out) - *out=fast_atof_move(floatAsString, ret); + *out = in; + return 0; + } + while (GMIO_TRUE) + { + uint32_t tmp = 0; + if ((*in >= '0') && (*in <= '9')) + tmp = (unsignedValue << 4u) + (*in - '0'); + else if ((*in >= 'A') && (*in <= 'F')) + tmp = (unsignedValue << 4u) + (*in - 'A') + 10; + else if ((*in >= 'a') && (*in <= 'f')) + tmp = (unsignedValue << 4u) + (*in - 'a') + 10; else - fast_atof_move(floatAsString, ret); - return ret; + break; + if (tmp= '0') && (*in <= '7')) + tmp = (unsignedValue << 3u) + (*in - '0'); + else + break; + if (tmp= '0') && ( *in <= '9' ) ) + { + /* If it looks like we're going to overflow, bail out + now and start using floating point. */ + if (intValue >= MAX_SAFE_U32_VALUE) + break; + intValue = (intValue * 10) + (*in - '0'); + ++in; + } + floatValue = (gmio_float32_t)intValue; + /* If there are any digits left to parse, then we need to use + * floating point arithmetic from here. */ + while ( ( *in >= '0') && ( *in <= '9' ) ) + { + floatValue = (floatValue * 10.f) + (gmio_float32_t)(*in - '0'); + ++in; + if (floatValue > FLT_MAX) /* Just give up. */ + break; + } + if (out) + *out = in; + return floatValue; +} + +/*! Provides a fast function for converting a string into a float. + * + * This is not guaranteed to be as accurate as atof(), but is + * approximately 6 to 8 times as fast. + * \param[in] in The string to convert. + * \param[out] result The resultant float will be written here. + * \return Pointer to the first character in the string that wasn't used + * to create the float value. + */ +GMIO_INLINE const char* fast_atof_move(const char* in, gmio_float32_t* result) +{ + const gmio_bool_t negative = ('-' == *in); + gmio_float32_t value = 0.f; + + /* Please run the regression test when making any modifications to this + * function. */ + *result = 0.f; + if (!in) + return 0; + if (negative || ('+'==*in)) + ++in; + value = strtof10(in, &in); + if ( is_local_decimal_point(*in) ) + { + const char* afterDecimal = ++in; + const gmio_float32_t decimal = strtof10(in, &afterDecimal); + value += decimal * fast_atof_table[afterDecimal - in]; + in = afterDecimal; + } + if ('e' == *in || 'E' == *in) + { + ++in; + /* Assume that the exponent is a whole number. + * strtol10() will deal with both + and - signs, + * but calculate as gmio_float32_t to prevent overflow at FLT_MAX */ + value *= +#ifdef GMIO_HAVE_POWF_FUNC + powf(10.f, (gmio_float32_t)strtol10(in, &in)); +#else + (gmio_float32_t)pow(10., (gmio_float64_t)strtol10(in, &in)); #endif + } + *result = negative?-value:value; + return in; +} +/*! Convert a string to a floating point number + * + * \param floatAsString The string to convert. + * \param out Optional pointer to the first character in the string that + * wasn't used to create the float value. + * \result Float value parsed from the input string + */ +GMIO_INLINE float fast_atof(const char* floatAsString, const char** out) +{ + float ret; + if (out) + *out=fast_atof_move(floatAsString, &ret); + else + fast_atof_move(floatAsString, &ret); + return ret; +}