gmio_core: adapt fast_atof.h for ISO-C90

This commit is contained in:
Hugues Delorme 2015-03-23 18:27:57 +01:00
parent ba87db052f
commit 83a8b004f1

View File

@ -1,23 +1,27 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt /* Copyright (C) 2002-2012 Nikolaus Gebhardt
// 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_ATOF_H_INCLUDED__ #include "../global.h"
#define __FAST_ATOF_H_INCLUDED__
#include "irrMath.h" #include <float.h>
#include "irrString.h" #include <limits.h>
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
//! Selection of characters which count as decimal point in fast_atof * the float-to-string code used there has to be rewritten first.
// 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[] = { '.' };
IRRLICHT_API extern irr::core::stringc 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] = { const float fast_atof_table[17] = {
0.f, 0.f,
0.1f, 0.1f,
@ -38,91 +42,92 @@ const float fast_atof_table[17] = {
0.0000000000000001f 0.0000000000000001f
}; };
//! Convert a simple string of base 10 digits into an unsigned 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. No leading chars are *
allowed, only digits 0 to 9. Parsing stops at the first non-digit. * \param[in] in: The string of digits to convert. No leading chars are
\param[out] out: (optional) If provided, it will be set to point at the * allowed, only digits 0 to 9. Parsing stops at the first non-digit.
first character not used in the calculation. * \param[out] out: (optional) If provided, it will be set to point at the
\return The unsigned integer value of the digits. If the string specifies * first character not used in the calculation.
too many digits to encode in an u32 then INT_MAX will be returned. * \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.
inline u32 strtoul10(const char* in, const char** out=0) */
GMIO_INLINE uint32_t strtoul10(const char* in, const char** out)
{ {
gmio_bool_t overflow=GMIO_FALSE;
uint32_t unsignedValue = 0;
if (!in) if (!in)
{ {
if (out) if (out)
*out = in; *out = in;
return 0; return 0;
} }
bool overflow=false;
u32 unsignedValue = 0;
while ( ( *in >= '0') && ( *in <= '9' )) while ( ( *in >= '0') && ( *in <= '9' ))
{ {
const u32 tmp = ( unsignedValue * 10 ) + ( *in - '0' ); const uint32_t tmp = ( unsignedValue * 10 ) + ( *in - '0' );
if (tmp<unsignedValue) if (tmp<unsignedValue)
{ {
unsignedValue=(u32)0xffffffff; unsignedValue=(uint32_t)0xffffffff;
overflow=true; overflow=GMIO_TRUE;
} }
if (!overflow) if (!overflow)
unsignedValue = tmp; unsignedValue = tmp;
++in; ++in;
} }
if (out) if (out)
*out = in; *out = in;
return unsignedValue; return unsignedValue;
} }
//! Convert a simple string of base 10 digits into a signed 32 bit integer. /*! 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 * \param[in] in: The string of digits to convert. Only a leading - or +
non-digit. * followed by digits 0 to 9 will be considered. Parsing stops at the first
\param[out] out: (optional) If provided, it will be set to point at the * non-digit.
first character not used in the calculation. * \param[out] out: (optional) If provided, it will be set to point at the
\return The signed integer value of the digits. If the string specifies * first character not used in the calculation.
too many digits to encode in an s32 then +INT_MAX or -INT_MAX will be * \return The signed integer value of the digits. If the string specifies
returned. * too many digits to encode in an int32_t then +INT_MAX or -INT_MAX will be
*/ * returned.
inline s32 strtol10(const char* in, const char** out=0) */
GMIO_INLINE int32_t strtol10(const char* in, const char** out)
{ {
const gmio_bool_t negative = ('-' == *in);
uint32_t unsignedValue = 0;
if (!in) if (!in)
{ {
if (out) if (out)
*out = in; *out = in;
return 0; return 0;
} }
const bool negative = ('-' == *in);
if (negative || ('+' == *in)) if (negative || ('+' == *in))
++in; ++in;
unsignedValue = strtoul10(in,out);
const u32 unsignedValue = strtoul10(in,out); if (unsignedValue > (uint32_t)INT_MAX)
if (unsignedValue > (u32)INT_MAX)
{ {
if (negative) if (negative)
return (s32)INT_MIN; return (int32_t)INT_MIN;
else else
return (s32)INT_MAX; return (int32_t)INT_MAX;
} }
else else
{ {
if (negative) if (negative)
return -((s32)unsignedValue); return -((int32_t)unsignedValue);
else else
return (s32)unsignedValue; return (int32_t)unsignedValue;
} }
} }
//! Convert a hex-encoded character to an unsigned integer. /*! 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. * \param[in] in The digit to convert. Only digits 0 to 9 and chars A-F,a-f
\return The unsigned integer value of the digit. 0xffffffff if the input is * will be considered.
not hex * \return The unsigned integer value of the digit. 0xffffffff if the input is
*/ * not hex
inline u32 ctoul16(char in) */
GMIO_INLINE uint32_t ctoul16(char in)
{ {
if (in >= '0' && in <= '9') if (in >= '0' && in <= '9')
return in - '0'; return in - '0';
@ -134,29 +139,30 @@ inline u32 ctoul16(char in)
return 0xffffffff; return 0xffffffff;
} }
//! Convert a simple string of base 16 digits into an unsigned 32 bit integer. /*! 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 * \param[in] in: The string of digits to convert. No leading chars are
at the first illegal char. * allowed, only digits 0 to 9 and chars A-F,a-f are allowed. Parsing stops
\param[out] out: (optional) If provided, it will be set to point at the * at the first illegal char.
first character not used in the calculation. * \param[out] out: (optional) If provided, it will be set to point at the
\return The unsigned integer value of the digits. If the string specifies * first character not used in the calculation.
too many digits to encode in an u32 then INT_MAX will be returned. * \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.
inline u32 strtoul16(const char* in, const char** out=0) */
GMIO_INLINE uint32_t strtoul16(const char* in, const char** out)
{ {
gmio_bool_t overflow=GMIO_FALSE;
uint32_t unsignedValue = 0;
if (!in) if (!in)
{ {
if (out) if (out)
*out = in; *out = in;
return 0; return 0;
} }
while (GMIO_TRUE)
bool overflow=false;
u32 unsignedValue = 0;
while (true)
{ {
u32 tmp = 0; uint32_t tmp = 0;
if ((*in >= '0') && (*in <= '9')) if ((*in >= '0') && (*in <= '9'))
tmp = (unsignedValue << 4u) + (*in - '0'); tmp = (unsignedValue << 4u) + (*in - '0');
else if ((*in >= 'A') && (*in <= 'F')) else if ((*in >= 'A') && (*in <= 'F'))
@ -167,73 +173,70 @@ inline u32 strtoul16(const char* in, const char** out=0)
break; break;
if (tmp<unsignedValue) if (tmp<unsignedValue)
{ {
unsignedValue=(u32)INT_MAX; unsignedValue=(uint32_t)INT_MAX;
overflow=true; overflow=GMIO_TRUE;
} }
if (!overflow) if (!overflow)
unsignedValue = tmp; unsignedValue = tmp;
++in; ++in;
} }
if (out) if (out)
*out = in; *out = in;
return unsignedValue; return unsignedValue;
} }
/*! Convert a simple string of base 8 digits into an unsigned 32 bit integer.
//! 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 * \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 * allowed, only digits 0 to 7 are allowed. Parsing stops at the first illegal
char. * char.
\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 character not used in the calculation. * first character not used in the calculation.
\return The unsigned integer value of the digits. If the string specifies * \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. * too many digits to encode in an uint32_t then INT_MAX will be returned.
*/ */
inline u32 strtoul8(const char* in, const char** out=0) GMIO_INLINE uint32_t strtoul8(const char* in, const char** out)
{ {
gmio_bool_t overflow=GMIO_FALSE;
uint32_t unsignedValue = 0;
if (!in) if (!in)
{ {
if (out) if (out)
*out = in; *out = in;
return 0; return 0;
} }
while (GMIO_TRUE)
bool overflow=false;
u32 unsignedValue = 0;
while (true)
{ {
u32 tmp = 0; uint32_t tmp = 0;
if ((*in >= '0') && (*in <= '7')) if ((*in >= '0') && (*in <= '7'))
tmp = (unsignedValue << 3u) + (*in - '0'); tmp = (unsignedValue << 3u) + (*in - '0');
else else
break; break;
if (tmp<unsignedValue) if (tmp<unsignedValue)
{ {
unsignedValue=(u32)INT_MAX; unsignedValue=(uint32_t)INT_MAX;
overflow=true; overflow=GMIO_TRUE;
} }
if (!overflow) if (!overflow)
unsignedValue = tmp; unsignedValue = tmp;
++in; ++in;
} }
if (out) if (out)
*out = in; *out = in;
return unsignedValue; return unsignedValue;
} }
//! Convert a C-style prefixed string (hex, oct, integer) into an unsigned 32 bit integer. /*! Convert a C-style prefixed string (hex, oct, integer) into an unsigned
/** \param[in] in The string of digits to convert. If string starts with 0x the * 32 bit integer.
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[in] in The string of digits to convert. If string starts with 0x the
\param[out] out (optional) If provided, it will be set to point at the * hex parser is used, if only leading 0 is used, oct parser is used. In all
first character not used in the calculation. * other cases, the usual unsigned parser is used.
\return The unsigned integer value of the digits. If the string specifies * \param[out] out (optional) If provided, it will be set to point at the
too many digits to encode in an u32 then INT_MAX will be returned. * first character not used in the calculation.
*/ * \return The unsigned integer value of the digits. If the string specifies
inline u32 strtoul_prefix(const char* in, const char** out=0) * too many digits to encode in an uint32_t then INT_MAX will be returned.
*/
GMIO_INLINE uint32_t strtoul_prefix(const char* in, const char** out)
{ {
if (!in) if (!in)
{ {
@ -246,119 +249,113 @@ inline u32 strtoul_prefix(const char* in, const char** out=0)
return strtoul10(in,out); return strtoul10(in,out);
} }
//! 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, *
including sign characters or a decimal point. * Only digits 0 to 9 are parsed. Parsing stops at any other character,
\param in: the sequence of digits to convert. * including sign characters or a decimal point.
\param out: (optional) will be set to point at the first non-converted * \param in: the sequence of digits to convert.
character. * \param out: (optional) will be set to point at the first non-converted
\return The whole positive floating point representation of the digit * character.
sequence. * \return The whole positive floating point representation of the digit
*/ * sequence.
inline f32 strtof10(const char* in, const char** out = 0) */
GMIO_INLINE gmio_float32_t strtof10(const char* in, const char** out)
{ {
const uint32_t MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10;
uint32_t intValue = 0;
gmio_float32_t floatValue = 0.f;
if (!in) if (!in)
{ {
if (out) if (out)
*out = in; *out = in;
return 0.f; return 0.f;
} }
/* Use integer arithmetic for as long as possible, for speed
const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10; * and precision. */
u32 intValue = 0;
// Use integer arithmetic for as long as possible, for speed
// and precision.
while ( ( *in >= '0') && ( *in <= '9' ) ) while ( ( *in >= '0') && ( *in <= '9' ) )
{ {
// 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 = (gmio_float32_t)intValue;
f32 floatValue = (f32)intValue; /* If there are any digits left to parse, then we need to use
* floating point arithmetic from here. */
// If there are any digits left to parse, then we need to use
// 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) + (gmio_float32_t)(*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 *
approximately 6 to 8 times as fast. * This is not guaranteed to be as accurate as atof(), but is
\param[in] in The string to convert. * approximately 6 to 8 times as fast.
\param[out] result The resultant float will be written here. * \param[in] in The string to convert.
\return Pointer to the first character in the string that wasn't used * \param[out] result The resultant float will be written here.
to create the float value. * \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) */
GMIO_INLINE const char* fast_atof_move(const char* in, gmio_float32_t* result)
{ {
// Please run the regression test when making any modifications to this function. const gmio_bool_t negative = ('-' == *in);
gmio_float32_t value = 0.f;
result = 0.f; /* Please run the regression test when making any modifications to this
* function. */
*result = 0.f;
if (!in) if (!in)
return 0; return 0;
const bool negative = ('-' == *in);
if (negative || ('+'==*in)) if (negative || ('+'==*in))
++in; ++in;
value = strtof10(in, &in);
f32 value = strtof10(in, &in); if ( is_local_decimal_point(*in) )
if ( LOCALE_DECIMAL_POINTS.findFirst(*in) >= 0 )
{ {
const char* afterDecimal = ++in; const char* afterDecimal = ++in;
const f32 decimal = strtof10(in, &afterDecimal); const gmio_float32_t decimal = strtof10(in, &afterDecimal);
value += decimal * fast_atof_table[afterDecimal - in]; value += decimal * fast_atof_table[afterDecimal - in];
in = afterDecimal; in = afterDecimal;
} }
if ('e' == *in || 'E' == *in) if ('e' == *in || 'E' == *in)
{ {
++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 calculate as f32 to prevent overflow at FLT_MAX * but calculate as gmio_float32_t to prevent overflow at FLT_MAX */
value *= powf(10.f, (f32)strtol10(in, &in)); 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;
result = negative?-value: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 out Optional pointer to the first character in the string that * \param floatAsString The string to convert.
wasn't used to create the float value. * \param out Optional pointer to the first character in the string that
\result Float value parsed from the input string * 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) */
GMIO_INLINE float fast_atof(const char* floatAsString, const char** out)
{ {
float ret; float ret;
if (out) if (out)
*out=fast_atof_move(floatAsString, ret); *out=fast_atof_move(floatAsString, &ret);
else else
fast_atof_move(floatAsString, ret); fast_atof_move(floatAsString, &ret);
return ret; return ret;
} }
} // end namespace core
} // end namespace irr
#endif