mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-18 00:08:20 +01:00
8310a3fbad
git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6000 dfc29bdd-3216-0410-991c-e03cc46cb475
375 lines
9.8 KiB
C++
375 lines
9.8 KiB
C++
// 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 "irrMath.h"
|
|
#include "irrString.h"
|
|
|
|
namespace irr
|
|
{
|
|
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;
|
|
|
|
#define IRR_ATOF_TABLE_SIZE 17
|
|
// we write [IRR_ATOF_TABLE_SIZE] 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
|
|
};
|
|
|
|
//! 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)
|
|
{
|
|
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<unsignedValue)
|
|
{
|
|
unsignedValue=(u32)0xffffffff;
|
|
overflow=true;
|
|
}
|
|
if (!overflow)
|
|
unsignedValue = tmp;
|
|
++in;
|
|
}
|
|
|
|
if (out)
|
|
*out = in;
|
|
|
|
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)
|
|
{
|
|
if (out)
|
|
*out = in;
|
|
return 0;
|
|
}
|
|
|
|
const bool negative = ('-' == *in);
|
|
if (negative || ('+' == *in))
|
|
++in;
|
|
|
|
const u32 unsignedValue = strtoul10(in,out);
|
|
if (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 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';
|
|
else
|
|
return 0xffffffff;
|
|
}
|
|
|
|
//! 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)
|
|
{
|
|
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;
|
|
if (tmp<unsignedValue)
|
|
{
|
|
unsignedValue=(u32)INT_MAX;
|
|
overflow=true;
|
|
}
|
|
if (!overflow)
|
|
unsignedValue = tmp;
|
|
++in;
|
|
}
|
|
|
|
if (out)
|
|
*out = in;
|
|
|
|
return unsignedValue;
|
|
}
|
|
|
|
//! Convert a simple string of base 8 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 7 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 strtoul8(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 <= '7'))
|
|
tmp = (unsignedValue << 3u) + (*in - '0');
|
|
else
|
|
break;
|
|
if (tmp<unsignedValue)
|
|
{
|
|
unsignedValue=(u32)INT_MAX;
|
|
overflow=true;
|
|
}
|
|
if (!overflow)
|
|
unsignedValue = tmp;
|
|
++in;
|
|
}
|
|
|
|
if (out)
|
|
*out = in;
|
|
|
|
return unsignedValue;
|
|
}
|
|
|
|
//! Convert a C-style prefixed string (hex, oct, integer) into an unsigned 32 bit integer.
|
|
/** \param[in] in The string of digits to convert. If string starts with 0x the
|
|
hex parser is used, if only leading 0 is used, oct parser is used. In all
|
|
other cases, the usual unsigned parser is used.
|
|
\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 strtoul_prefix(const char* in, const char** out=0)
|
|
{
|
|
if (!in)
|
|
{
|
|
if (out)
|
|
*out = in;
|
|
return 0;
|
|
}
|
|
if ('0'==in[0])
|
|
return ('x'==in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out));
|
|
return strtoul10(in,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)
|
|
{
|
|
if (!in)
|
|
{
|
|
if (out)
|
|
*out = in;
|
|
return 0.f;
|
|
}
|
|
|
|
const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10;
|
|
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;
|
|
}
|
|
|
|
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);
|
|
const size_t numDecimals = afterDecimal - in;
|
|
if (numDecimals < IRR_ATOF_TABLE_SIZE)
|
|
{
|
|
value += decimal * fast_atof_table[numDecimals];
|
|
}
|
|
else
|
|
{
|
|
value += decimal * (f32)pow(10.f, -(float)numDecimals);
|
|
}
|
|
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
|
|
// Using pow with float cast instead of powf as otherwise accuracy decreases.
|
|
value *= (f32)pow(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);
|
|
else
|
|
fast_atof_move(floatAsString, ret);
|
|
return ret;
|
|
}
|
|
|
|
} // end namespace core
|
|
} // end namespace irr
|
|
|
|
#endif
|
|
|