irrlicht/include/fast_atof.h

197 lines
4.8 KiB
C++

// Copyright (C) 2002-2008 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_A_TO_F_H_INCLUDED__
#define __FAST_A_TO_F_H_INCLUDED__
#include <stdlib.h>
#include "irrMath.h"
#include <float.h> // For FLT_MAX
#include <limits.h> // For INT_MAX / UINT_MAX
namespace irr
{
namespace core
{
// we write [16] here instead of [] to work around a swig bug
const float fast_atof_table[16] = {
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
};
//! 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)
return 0;
bool negative = false;
if('-' == *in)
{
negative = true;
++in;
}
else if('+' == *in)
++in;
u32 unsignedValue = 0;
while ( ( *in >= '0') && ( *in <= '9' ))
{
unsignedValue = ( unsignedValue * 10 ) + ( *in - '0' );
++in;
if(unsignedValue > (u32)INT_MAX)
{
unsignedValue = (u32)INT_MAX;
break;
}
}
if (out)
*out = in;
if(negative)
return -((s32)unsignedValue);
else
return (s32)unsignedValue;
}
//! 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)
{
if(!in)
return 0.f;
static const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10;
f32 floatValue = 0.f;
u32 intValue = 0;
// Use integer arithmetic for as long as possible, for speed
// and precision.
while ( ( *in >= '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 = (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] 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
out = 0.f;
if(!in)
return 0;
bool negative = false;
if(*in == '-')
{
negative = true;
++in;
}
f32 value = strtof10 ( in, &in );
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(negative)
out = -value;
else
out = value;
return in;
}
//! 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(floatAsString, ret);
return ret;
}
} // end namespace core
} // end namespace irr
#endif