From be5c1cd65dea4f4aa43724412879860d777a2ce8 Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Fri, 20 Nov 2015 11:19:32 +0100 Subject: [PATCH] gmio_core/internal: add gmio_parse_float32() --- src/gmio_core/internal/string_parse.h | 23 +++- .../internal/string_parse_fast_atof.h | 112 ++++++++++++++++++ tests/main_test_core.c | 2 + tests/test_core_internal.c | 31 ++++- 4 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 src/gmio_core/internal/string_parse_fast_atof.h diff --git a/src/gmio_core/internal/string_parse.h b/src/gmio_core/internal/string_parse.h index fdfdad2..ad8a42b 100644 --- a/src/gmio_core/internal/string_parse.h +++ b/src/gmio_core/internal/string_parse.h @@ -25,6 +25,8 @@ * To be used with API below. * It allows to iterate over a stream (until end is reached) as if it was a * string. + * + * TODO: rename to gmio_string_stream */ struct gmio_string_stream_fwd_iterator { @@ -34,8 +36,7 @@ struct gmio_string_stream_fwd_iterator const char* strbuff_ptr_at; /*!< Position indicator in buffer */ void* cookie; - void (*func_stream_read_hook)( - void* cookie, const gmio_string_t* str_buffer); + void (*func_stream_read_hook)(void* cookie, const gmio_string_t* strbuff); }; typedef struct gmio_string_stream_fwd_iterator @@ -89,6 +90,10 @@ gmio_bool_t gmio_checked_next_chars( gmio_string_stream_fwd_iterator_t* it, const char* str); #endif +/*! Parses float from string iterator \p it */ +GMIO_INLINE gmio_float32_t gmio_parse_float32( + gmio_string_stream_fwd_iterator_t* it); + /*! Converts C string \p str to float * * \retval 0 On success @@ -109,6 +114,7 @@ GMIO_INLINE gmio_float32_t gmio_to_float32(const char* str); #include "string_utils.h" #ifdef GMIO_STRINGPARSE_USE_FAST_ATOF # include "fast_atof.h" +# include "string_parse_fast_atof.h" #endif #include @@ -148,7 +154,6 @@ gmio_string_stream_fwd_iterator_t* gmio_move_next_char( return it; } - const char* gmio_skip_spaces( gmio_string_stream_fwd_iterator_t* it) { @@ -198,4 +203,16 @@ gmio_float32_t gmio_to_float32(const char* str) #endif } +gmio_float32_t gmio_parse_float32(gmio_string_stream_fwd_iterator_t* it) +{ +#if defined(GMIO_STRINGPARSE_USE_FAST_ATOF) + return gmio_fast_atof(it); +#else + char strbuff_ptr[64]; + gmio_string_t strbuff = { &strbuff_ptr[0], 0, sizeof(strbuff_ptr) }; + gmio_eat_word(it, &strbuff); + return (gmio_float32_t)atof(strbuff_ptr); +#endif +} + #endif /* GMIO_INTERNAL_STRING_PARSE_H */ diff --git a/src/gmio_core/internal/string_parse_fast_atof.h b/src/gmio_core/internal/string_parse_fast_atof.h new file mode 100644 index 0000000..3ff61af --- /dev/null +++ b/src/gmio_core/internal/string_parse_fast_atof.h @@ -0,0 +1,112 @@ +/* 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 + */ + +/* Adapted to ISO-C90 */ + +#ifndef GMIO_INTERNAL_STRING_PARSE_FAST_ATOF_H +#define GMIO_INTERNAL_STRING_PARSE_FAST_ATOF_H + +#include "fast_atof.h" +#include "string_parse.h" + +GMIO_INLINE uint32_t gmio_strtoul10(gmio_string_stream_fwd_iterator_t* it) +{ + unsigned int value = 0; + const char* in = gmio_current_char(it); + for (; in != NULL && gmio_ascii_isdigit(*in); in = gmio_next_char(it)) + value = (value * 10) + (*in - '0'); + return value; +} + +GMIO_INLINE int32_t gmio_strtol10(gmio_string_stream_fwd_iterator_t* it) +{ + const char* in = gmio_current_char(it); + const gmio_bool_t inv = (*in == '-'); + int value = 0; + if (inv || *in == '+') + in = gmio_next_char(it); + + value = gmio_strtoul10(it); + if (inv) + value = -value; + return value; +} + +struct gmio_strtof10_result +{ + float val; + unsigned char_diff; +}; +typedef struct gmio_strtof10_result gmio_strtof10_result_t; + +GMIO_INLINE gmio_strtof10_result_t gmio_strtof10( + gmio_string_stream_fwd_iterator_t* it) +{ + const char* in = gmio_current_char(it); + const uint32_t MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10; + uint32_t int_val = 0; + float float_val = 0.f; + unsigned char_diff = 0; + gmio_strtof10_result_t result; + + /* Use integer arithmetic for as long as possible, for speed and + * precision */ + for (; + in != NULL && gmio_ascii_isdigit(*in) && int_val < MAX_SAFE_U32_VALUE; + in = gmio_next_char(it)) + { + int_val = (int_val * 10) + (*in - '0'); + ++char_diff; + } + float_val = (float)int_val; + /* If there are any digits left to parse, then we need to use floating point + * arithmetic from here */ + for (; + in != NULL && gmio_ascii_isdigit(*in) && float_val <= FLT_MAX; + in = gmio_next_char(it)) + { + float_val = (float_val * 10) + (*in - '0'); + ++char_diff; + } + result.val = float_val; + result.char_diff = char_diff; + return result; +} + +GMIO_INLINE float gmio_fast_atof(gmio_string_stream_fwd_iterator_t* it) +{ + const char* in = gmio_current_char(it); + const gmio_bool_t negative = ('-' == *in); + float value = 0.f; + + /* Please run the regression test when making any modifications to this + * function. */ + if (negative || ('+' == *in)) + in = gmio_next_char(it); + value = gmio_strtof10(it).val; + in = gmio_current_char(it); + if (is_local_decimal_point(*in)) { + const gmio_strtof10_result_t decimal = + gmio_strtof10(gmio_move_next_char(it)); + value += decimal.val * fast_atof_table[decimal.char_diff]; + in = gmio_current_char(it); + } + if (in != NULL && ('e' == *in || 'E' == *in)) { + in = gmio_next_char(it); + /* Assume that the exponent is a whole number. + * strtol10() will deal with both + and - signs, + * but calculate as float to prevent overflow at FLT_MAX */ + value *= +#ifdef GMIO_HAVE_POWF_FUNC + powf(10.f, (float)gmio_strtol10(it)); +#else + (float)pow(10., (double)gmio_strtol10(it)); +#endif + } + return negative ? -value : value; +} + +#endif /* GMIO_INTERNAL_STRING_PARSE_FAST_ATOF_H */ diff --git a/tests/main_test_core.c b/tests/main_test_core.c index 4a62f96..af664b8 100644 --- a/tests/main_test_core.c +++ b/tests/main_test_core.c @@ -23,6 +23,7 @@ const char* test_core__stream(); const char* test_internal__byte_swap(); const char* test_internal__byte_codec(); const char* test_internal__fast_atof(); +const char* test_internal__gmio_fast_atof(); const char* test_internal__safe_cast(); const char* test_internal__string_parse(); const char* test_internal__string_utils(); @@ -43,6 +44,7 @@ const char* all_tests() UTEST_RUN(test_internal__byte_swap); UTEST_RUN(test_internal__byte_codec); UTEST_RUN(test_internal__fast_atof); + UTEST_RUN(test_internal__gmio_fast_atof); UTEST_RUN(test_internal__safe_cast); UTEST_RUN(test_internal__string_parse); UTEST_RUN(test_internal__string_utils); diff --git a/tests/test_core_internal.c b/tests/test_core_internal.c index 4e130d3..9ab6077 100644 --- a/tests/test_core_internal.c +++ b/tests/test_core_internal.c @@ -62,7 +62,7 @@ const char* test_internal__byte_codec() static gmio_bool_t gmio_test_calculation_atof(const char* value_str) { - const gmio_float32_t fast_value = fast_atof(value_str, NULL); + const gmio_float32_t fast_value = fast_atof(value_str); const gmio_float32_t std_value = (gmio_float32_t)strtod(value_str, NULL); const gmio_bool_t accurate = gmio_float32_equals_by_ulp(fast_value, std_value, 1); @@ -124,6 +124,35 @@ const char* test_internal__fast_atof() return NULL; } +const char* test_internal__gmio_fast_atof() +{ + const char fstr[] = "1234.567E05"; + const float f1 = fast_atof(fstr); + + { + char strbuff[2048] = {0}; + gmio_string_stream_fwd_iterator_t it = {0}; + gmio_stream_buffer_t streambuff = {0}; + gmio_stream_t stream = {0}; + float f2; + + streambuff.readonly_ptr = &fstr[0]; + streambuff.len = sizeof(fstr) - 1; + gmio_stream_set_buffer(&stream, &streambuff); + + it.stream = &stream; + it.strbuff.ptr = &strbuff[0]; + it.strbuff.max_len = sizeof(strbuff) - 1; + gmio_string_stream_fwd_iterator_init(&it); + + f2 = gmio_fast_atof(&it); + + UTEST_ASSERT(gmio_float32_equals_by_ulp(f1, f2, 1)); + } + + return NULL; +} + const char* test_internal__safe_cast() { #if GMIO_TARGET_ARCH_BIT_SIZE > 32