Commit 5507395c authored by Rogerborg's avatar Rogerborg

Fixing fast_atof() to handle long digit strings. See bug 1865300 for...

Fixing fast_atof() to handle long digit strings.  See bug 1865300 for regression test.  Tested in debug and release on MSVC++ 9.0 / Windows.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@1647 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 6fa8a63a
......@@ -7,6 +7,7 @@
#include <stdlib.h>
#include "irrMath.h"
#include <float.h> // For FLT_MAX
namespace irr
{
......@@ -33,77 +34,157 @@ const float fast_atof_table[16] = {
0.000000000000001f
};
inline u32 strtol10(const char* in, const char** out=0)
//! 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)
{
u32 value = 0;
if(!in)
return 0;
bool negative = false;
if('-' == *in)
{
negative = true;
++in;
}
else if('+' == *in)
++in;
u32 unsignedValue = 0;
while ( ( *in >= '0') && ( *in <= '9' ))
{
value = ( value * 10 ) + ( *in - '0' );
unsignedValue = ( unsignedValue * 10 ) + ( *in - '0' );
++in;
if(unsignedValue > (u32)INT_MAX)
{
unsignedValue = (u32)INT_MAX;
break;
}
}
if (out)
*out = in;
return value;
if(negative)
return -((s32)unsignedValue);
else
return (s32)unsignedValue;
}
//! Provides a fast function for converting a string into a float,
//! about 6 times faster than atof in win32.
// If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
inline const char* fast_atof_move( const char* c, float& out)
//! 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,
//! including sign characters or a decimal point.
//! \param in: the sequence of digits to convert.
//! \param out: (optional) will be set to point at the first non-converted character.
//! \return The whole positive floating point representation of the digit sequence.
inline f32 strtof10(const char* in, const char * * out = 0)
{
bool inv = false;
const char *t;
float f;
if(!in)
return 0.f;
static const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10;
f32 floatValue = 0.f;
u32 intValue = 0;
if (*c=='-')
// Use integer arithmetic for as long as possible, for speed
// and precision.
while ( ( *in >= '0') && ( *in <= '9' ) )
{
++c;
inv = true;
// 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;
}
//f = (float)strtol(c, &t, 10);
f = (float) strtol10 ( c, &c );
floatValue = (f32)intValue;
if (*c == '.')
// If there are any digits left to parse, then we need to use
// floating point arithmetic from here.
while ( ( *in >= '0') && ( *in <= '9' ) )
{
++c;
floatValue = ( floatValue * 10.f ) + (f32)( *in - '0' );
++in;
if(floatValue > FLT_MAX) // Just give up.
break;
}
//float pl = (float)strtol(c, &t, 10);
float pl = (float) strtol10 ( c, &t );
pl *= fast_atof_table[t-c];
if(out)
*out = in;
f += pl;
return floatValue;
}
c = t;
//! 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] out: The resultant float will be written here.
//! \return A pointer to the first character in the string that wasn't
//! use to create the float value.
inline const char* fast_atof_move( const char * in, f32 & out)
{
// 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
if (*c == 'e')
{
++c;
//float exp = (float)strtol(c, &t, 10);
bool einv = (*c=='-');
if (einv)
++c;
out = 0.f;
if(!in)
return 0;
bool negative = false;
if(*in == '-')
{
negative = true;
++in;
}
float exp = (float)strtol10(c, &c);
if (einv)
exp *= -1.0f;
f32 value = strtof10 ( in, &in );
f *= (float)pow(10.0f, exp);
}
if (*in == '.')
{
++in;
const char * afterDecimal;
f32 decimal = strtof10 ( in, &afterDecimal );
decimal *= fast_atof_table[afterDecimal - in];
value += decimal;
in = afterDecimal;
}
if ('e' == *in || 'E' == *in)
{
++in;
// Assume that the exponent is a whole number.
// strtol10() will deal with both + and - signs,
// but cast to (f32) to prevent overflow at FLT_MAX
value *= (f32)pow(10.0f, (f32)strtol10(in, &in));
}
if (inv)
f *= -1.0f;
if(negative)
out = -value;
else
out = value;
out = f;
return c;
return in;
}
inline float fast_atof(const char* c)
//! Convert a string to a floating point number
//! \param floatAsString: The string to convert.
inline float fast_atof(const char* floatAsString)
{
float ret;
fast_atof_move(c, ret);
fast_atof_move(floatAsString, ret);
return 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