Commit 3d6b88eb authored by hybrid's avatar hybrid

Add first round of patches from assimp fast_atof version. This code is...

Add first round of patches from assimp fast_atof version. This code is slightly slower than the original one, but for the first time works correctly according to the assumptions of the API docs. Especially stopping at the first non-supported character and returning properly MAX_INT or similar values. This obviously means that some more steps need to be done, but parsing shouldn't be the major performance bottleneck anyway.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@3672 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 4de19ba5
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// This file is part of the "Irrlicht Engine" and the "irrXML" project. // 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 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
#ifndef __FAST_A_TO_F_H_INCLUDED__ #ifndef __FAST_ATOF_H_INCLUDED__
#define __FAST_A_TO_F_H_INCLUDED__ #define __FAST_ATOF_H_INCLUDED__
#include "irrMath.h" #include "irrMath.h"
...@@ -33,66 +33,150 @@ const float fast_atof_table[17] = { ...@@ -33,66 +33,150 @@ const float fast_atof_table[17] = {
0.0000000000000001f 0.0000000000000001f
}; };
//! Convert a simple string of base 10 digits into a signed 32 bit integer. //! Convert a simple string of base 10 digits into an unsigned 32 bit integer.
//! \param[in] in: The string of digits to convert. Only a leading - or + followed /** \param[in] in: The string of digits to convert. No leading chars are
//! by digits 0 to 9 will be considered. Parsing stops at the allowed, only digits 0 to 9. Parsing stops at the first non-digit.
//! first non-digit. \param[out] out: (optional) If provided, it will be set to point at the
//! \param[out] out: (optional) If provided, it will be set to point at the first first character not used in the calculation.
//! character not used in the calculation. \return The unsigned integer value of the digits. If the string specifies
//! \return The signed integer value of the digits. If the string specifies too many too many digits to encode in an u32 then INT_MAX will be returned.
//! digits to encode in an s32 then +INT_MAX or -INT_MAX will be returned. */
inline s32 strtol10(const char* in, const char** out=0) inline u32 strtoul10(const char* in, const char** out=0)
{ {
if(!in) if (!in)
{
if (out)
*out = in;
return 0; return 0;
}
bool negative = false; bool overflow=false;
if('-' == *in) u32 unsignedValue = 0;
while ( ( *in >= '0') && ( *in <= '9' ))
{ {
negative = true; const u32 tmp = ( unsignedValue * 10 ) + ( *in - '0' );
if (tmp<unsignedValue)
{
unsignedValue=(u32)0xffffffff;
overflow=true;
}
if (!overflow)
unsignedValue = tmp;
++in; ++in;
} }
else if('+' == *in)
++in;
u32 unsignedValue = 0; if (out)
*out = in;
while ( ( *in >= '0') && ( *in <= '9' )) return unsignedValue;
}
//! Convert a simple string of base 10 digits into a signed 32 bit integer.
/** \param[in] in: The string of digits to convert. Only a leading - or +
followed by digits 0 to 9 will be considered. 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 signed integer value of the digits. If the string specifies
too many digits to encode in an s32 then +INT_MAX or -INT_MAX will be
returned.
*/
inline s32 strtol10(const char* in, const char** out=0)
{
if (!in)
{ {
unsignedValue = ( unsignedValue * 10 ) + ( *in - '0' ); if (out)
*out = in;
return 0;
}
const bool negative = ('-' == *in);
if (negative || ('+' == *in))
++in; ++in;
if(unsignedValue > (u32)INT_MAX) const u32 unsignedValue = strtoul10(in,out);
{ if (unsignedValue > (u32)INT_MAX)
unsignedValue = (u32)INT_MAX; {
if (negative)
return (s32)INT_MIN;
else
return (s32)INT_MAX;
}
else
{
if (negative)
return -((s32)unsignedValue);
else
return (s32)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 s32 strtoul16(const char* in, const char** out=0)
{
if (!in)
{
if (out)
*out = in;
return 0;
}
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; break;
if (tmp<unsignedValue)
{
unsignedValue=(u32)INT_MAX;
overflow=false;
} }
if (!overflow)
unsignedValue = tmp;
++in;
} }
if (out) if (out)
*out = in; *out = in;
if(negative) return unsignedValue;
return -((s32)unsignedValue);
else
return (s32)unsignedValue;
} }
//! Converts a sequence of digits into a whole positive floating point value. //! Converts a sequence of digits into a whole positive floating point value.
//! Only digits 0 to 9 are parsed. Parsing stops at any other character, /** Only digits 0 to 9 are parsed. Parsing stops at any other character,
//! including sign characters or a decimal point. including sign characters or a decimal point.
//! \param in: the sequence of digits to convert. \param in: the sequence of digits to convert.
//! \param out: (optional) will be set to point at the first non-converted character. \param out: (optional) will be set to point at the first non-converted
//! \return The whole positive floating point representation of the digit sequence. character.
inline f32 strtof10(const char* in, const char * * out = 0) \return The whole positive floating point representation of the digit
sequence.
*/
inline f32 strtof10(const char* in, const char** out = 0)
{ {
if(out) if (!in)
*out = in; {
if (out)
if(!in) *out = in;
return 0.f; return 0.f;
}
static const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10; const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10;
f32 floatValue = 0.f;
u32 intValue = 0; u32 intValue = 0;
// Use integer arithmetic for as long as possible, for speed // Use integer arithmetic for as long as possible, for speed
...@@ -101,66 +185,59 @@ inline f32 strtof10(const char* in, const char * * out = 0) ...@@ -101,66 +185,59 @@ inline f32 strtof10(const char* in, const char * * out = 0)
{ {
// If it looks like we're going to overflow, bail out // If it looks like we're going to overflow, bail out
// now and start using floating point. // now and start using floating point.
if(intValue >= MAX_SAFE_U32_VALUE) if (intValue >= MAX_SAFE_U32_VALUE)
break; break;
intValue = ( intValue * 10) + ( *in - '0' ); intValue = (intValue * 10) + (*in - '0');
++in; ++in;
} }
floatValue = (f32)intValue; f32 floatValue = (f32)intValue;
// If there are any digits left to parse, then we need to use // If there are any digits left to parse, then we need to use
// floating point arithmetic from here. // floating point arithmetic from here.
while ( ( *in >= '0') && ( *in <= '9' ) ) while ( ( *in >= '0') && ( *in <= '9' ) )
{ {
floatValue = ( floatValue * 10.f ) + (f32)( *in - '0' ); floatValue = (floatValue * 10.f) + (f32)(*in - '0');
++in; ++in;
if(floatValue > FLT_MAX) // Just give up. if (floatValue > FLT_MAX) // Just give up.
break; break;
} }
if(out) if (out)
*out = in; *out = in;
return floatValue; return floatValue;
} }
//! Provides a fast function for converting a string into a float. //! Provides a fast function for converting a string into a float.
//! This is not guaranteed to be as accurate as atof(), but is /** This is not guaranteed to be as accurate as atof(), but is
//! approximately 6 to 8 times as fast. approximately 6 to 8 times as fast.
//! \param[in] in: The string to convert. \param[in] in: The string to convert.
//! \param[out] out: The resultant float will be written here. \param[out] out: The resultant float will be written here.
//! \return A pointer to the first character in the string that wasn't \return A pointer to the first character in the string that wasn't
//! use to create the float value. used to create the float value.
inline const char* fast_atof_move( const char * in, f32 & out) */
inline const char* fast_atof_move(const char * in, f32& result)
{ {
// Please run this regression test when making any modifications to this function: // Please run this regression test when making any modifications to this function:
// https://sourceforge.net/tracker/download.php?group_id=74339&atid=540676&file_id=298968&aid=1865300 // https://sourceforge.net/tracker/download.php?group_id=74339&atid=540676&file_id=298968&aid=1865300
out = 0.f; result = 0.f;
if(!in) if (!in)
return 0; return 0;
bool negative = false; const bool negative = ('-' == *in);
if(*in == '-') if (negative)
{
negative = true;
++in; ++in;
}
f32 value = strtof10 ( in, &in ); f32 value = strtof10(in, &in);
if (*in == '.') if ('.' == *in)
{ {
++in; const char* afterDecimal = ++in;
const f32 decimal = strtof10(in, &afterDecimal);
const char * afterDecimal = in; value += decimal * fast_atof_table[afterDecimal - in];
f32 decimal = strtof10 ( in, &afterDecimal );
decimal *= fast_atof_table[afterDecimal - in];
value += decimal;
in = afterDecimal; in = afterDecimal;
} }
...@@ -169,20 +246,17 @@ inline const char* fast_atof_move( const char * in, f32 & out) ...@@ -169,20 +246,17 @@ inline const char* fast_atof_move( const char * in, f32 & out)
++in; ++in;
// Assume that the exponent is a whole number. // Assume that the exponent is a whole number.
// strtol10() will deal with both + and - signs, // strtol10() will deal with both + and - signs,
// but cast to (f32) to prevent overflow at FLT_MAX // but calculate as f32 to prevent overflow at FLT_MAX
value *= (f32)pow(10.0f, (f32)strtol10(in, &in)); value *= powf(10.f, (f32)strtol10(in, &in));
} }
if(negative) result = negative?-value:value;
out = -value;
else
out = value;
return in; return in;
} }
//! Convert a string to a floating point number //! Convert a string to a floating point number
//! \param floatAsString: The string to convert. /** \param floatAsString: The string to convert.
*/
inline float fast_atof(const char* floatAsString) inline float fast_atof(const char* floatAsString)
{ {
float ret; float ret;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment