From e22c027544ab3a04f5abcb76ef5a83985fa540d6 Mon Sep 17 00:00:00 2001 From: "allen.barnett@paperbirchsoftware.com" Date: Mon, 7 Aug 2023 08:39:06 -0400 Subject: [PATCH] Allow fast_float to parse strings accepted by the Fortran internal read function. --- include/fast_float/ascii_number.h | 12 +++++-- include/fast_float/float_common.h | 1 + tests/CMakeLists.txt | 2 +- tests/fortran.cpp | 57 +++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/fortran.cpp diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 9afcdc4e..5af47de6 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -328,9 +328,17 @@ parsed_number_string_t parse_number_string(UC const *p, UC const * pend, par return answer; } int64_t exp_number = 0; // explicit exponential part - if ((fmt & chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) { + if ( ((fmt & chars_format::scientific) && + (p != pend) && + ((UC('e') == *p) || (UC('E') == *p))) + || + ((fmt & chars_format::fortran) && + (p != pend) && + ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) { UC const * location_of_e = p; - ++p; + if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) { + ++p; + } bool neg_exp = false; if ((p != pend) && (UC('-') == *p)) { neg_exp = true; diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 4a290f48..1282f0f1 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -16,6 +16,7 @@ enum chars_format { scientific = 1 << 0, fixed = 1 << 2, hex = 1 << 3, + fortran = 1 << 4 | fixed | scientific, general = fixed | scientific }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 37f6c7f8..296e58ad 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -72,7 +72,7 @@ fast_float_add_cpp_test(long_test) fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(string_test) - +fast_float_add_cpp_test(fortran) option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF) diff --git a/tests/fortran.cpp b/tests/fortran.cpp new file mode 100644 index 00000000..d7de55db --- /dev/null +++ b/tests/fortran.cpp @@ -0,0 +1,57 @@ +/* + * Exercise the Fortran conversion option. + */ +#include +#include +#include + +#define FASTFLOAT_ALLOWS_LEADING_PLUS + +#include "fast_float/fast_float.h" + +int main () +{ + const std::vector expected{ 10000, 1000, 100, 10, 1, .1, .01, .001, .0001 }; + const std::vector fmt1{ "1+4", "1+3", "1+2", "1+1", "1+0", "1-1", "1-2", + "1-3", "1-4" }; + const std::vector fmt2{ "1d+4", "1d+3", "1d+2", "1d+1", "1d+0", "1d-1", + "1d-2", "1d-3", "1d-4" }; + const std::vector fmt3{ "+1+4", "+1+3", "+1+2", "+1+1", "+1+0", "+1-1", + "+1-2", "+1-3", "+1-4" }; + const fast_float::parse_options options{ fast_float::chars_format::fortran }; + + for ( auto const& f : fmt1 ) { + auto d{ std::distance( &fmt1[0], &f ) }; + double result; + auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result, + options ) }; + if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) { + std::cerr << "parsing failure on " << f << std::endl; + return EXIT_FAILURE; + } + } + + for ( auto const& f : fmt2 ) { + auto d{ std::distance( &fmt2[0], &f ) }; + double result; + auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result, + options ) }; + if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) { + std::cerr << "parsing failure on " << f << std::endl; + return EXIT_FAILURE; + } + } + + for ( auto const& f : fmt3 ) { + auto d{ std::distance( &fmt3[0], &f ) }; + double result; + auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result, + options ) }; + if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) { + std::cerr << "parsing failure on " << f << std::endl; + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +}