diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 4878b32..b4728f3 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -731,6 +732,136 @@ namespace boost } } + namespace detail + { + /* Returns true and sets the correct value if found NaN or Inf. */ + template + inline bool parse_inf_nan_impl(const CharT* begin, const CharT* end, T& value + , const CharT* lc_NAN, const CharT* lc_nan + , const CharT* lc_INFINITY, const CharT* lc_infinity + , const CharT opening_brace, const CharT closing_brace) + { + using namespace std; + const wchar_t minus = lcast_char_constants::minus; + const wchar_t plus = lcast_char_constants::plus; + const int inifinity_size = 8; + + bool has_minus = false; + /* Parsing +/- */ + if( *begin == minus) + { + ++ begin; + has_minus = true; + } + else if( *begin == plus ) ++begin; + + if( end-begin < 3 ) return false; + if( !memcmp(begin, lc_nan, 3*sizeof(CharT)) || !memcmp(begin, lc_NAN, 3*sizeof(CharT)) ) + { + begin += 3; + if (end != begin) /* It is 'nan(...)' or some bad input*/ + { + if(end-begin<2) return false; // bad input + -- end; + if( *begin != opening_brace || *end != closing_brace) return false; // bad input + } + + if( !has_minus ) value = std::numeric_limits::quiet_NaN(); + else value = -std::numeric_limits::quiet_NaN(); + return true; + } else + if (( /* 'INF' or 'inf' */ + end-begin==3 + && + (!memcmp(begin, lc_infinity, 3*sizeof(CharT)) || !memcmp(begin, lc_INFINITY, 3*sizeof(CharT))) + ) + || + ( /* 'INFINITY' or 'infinity' */ + end-begin==inifinity_size + && + (!memcmp(begin, lc_infinity, inifinity_size)|| !memcmp(begin, lc_INFINITY, inifinity_size)) + ) + ) + { + if( !has_minus ) value = std::numeric_limits::infinity(); + else value = -std::numeric_limits::infinity(); + return true; + } + + return false; + } + +#ifndef BOOST_LCAST_NO_WCHAR_T + template + bool parse_inf_nan(const wchar_t* begin, const wchar_t* end, T& value) + { + return parse_inf_nan_impl(begin, end, value + , L"NAN", L"nan" + , L"INFINITY", L"infinity" + , L'(', L')'); + } +#endif + + template + bool parse_inf_nan(const CharT* begin, const CharT* end, T& value) + { + return parse_inf_nan_impl(begin, end, value + , "NAN", "nan" + , "INFINITY", "infinity" + , '(', ')'); + } + + template + bool put_inf_nan(wchar_t* begin, wchar_t*& end, const T& value) + { + using namespace std; + if (value != value) + { + memcpy(begin,L"nan", sizeof(L"nan")); + end = begin + 3; + return true; + } else if ( value > numeric_limits::max() ) + { + memcpy(begin,L"inf", sizeof(L"inf")); + end = begin + 3; + return true; + } else if ( value < -numeric_limits::max() ) + { + memcpy(begin,L"-inf", sizeof(L"-inf")); + end = begin + 4; + return true; + } + + return false; + } + + template + bool put_inf_nan(CharT* begin, CharT*& end, const T& value) + { + using namespace std; + if (value != value) + { + memcpy(begin,"nan", sizeof("nan")); + end = begin + 3; + return true; + } else if ( value > numeric_limits::max() ) + { + memcpy(begin,"inf", sizeof("inf")); + end = begin + 3; + return true; + } else if ( value < -numeric_limits::max() ) + { + memcpy(begin,"-inf", sizeof("-inf")); + end = begin + 4; + return true; + } + + return false; + } + + } + + namespace detail // lcast_ret_float { template @@ -782,6 +913,8 @@ namespace boost value = 0.0; + if (parse_inf_nan(begin, end, value)) return true; + typedef typename Traits::int_type int_type; typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type::type mantissa_type; int_type const zero = Traits::to_int_type(czero); @@ -1104,6 +1237,7 @@ namespace boost template bool lcast_put(const OutputStreamable& input) { + if(put_inf_nan(start, finish, input)) return true; this->setp(start, finish); std::basic_ostream stream(static_cast(this)); lcast_set_precision(stream, static_cast(0)); @@ -1342,10 +1476,13 @@ namespace boost // Not optimised converter template bool float_types_converter_internal(T& output, int /*tag*/) { + + if (parse_inf_nan(start, finish, output)) return true; + bool return_value = convert_using_base_class(output); /* Some compilers and libraries successfully - * parse 'inf', 'INFINITY', '1.0E', '1.0E-'... + * parse '1.0E', '1.0E-'... * We are trying to provide a unified behaviour, * so we just forbid such conversions (as some * of the most popular compilers/libraries do) @@ -1356,10 +1493,7 @@ namespace boost CharT const lowercase_e = lcast_char_constants::lowercase_e; if ( return_value && ( - output > (std::numeric_limits::max)() // +inf - || output < -(std::numeric_limits::max)() // -inf - || output != output // NaN - || *(finish-1) == lowercase_e // 1.0e + *(finish-1) == lowercase_e // 1.0e || *(finish-1) == capital_e // 1.0E || *(finish-1) == minus // 1.0e- or 1.0E- || *(finish-1) == plus // 1.0e+ or 1.0E+ diff --git a/lexical_cast.htm b/lexical_cast.htm index 2079a0f..35c6e58 100644 --- a/lexical_cast.htm +++ b/lexical_cast.htm @@ -269,6 +269,7 @@ Eliminate an overhead of std::locale if your program runs in the "C

Changes

July 2011:

    +
  • Added code to work with Inf and NaN on any platform.
  • Better performance and less memory usage for conversions to float type (and to double type, if sizeof(double)<sizeof(long double)).

May 2011:

diff --git a/lexical_cast_test.cpp b/lexical_cast_test.cpp index afa16a6..244addb 100644 --- a/lexical_cast_test.cpp +++ b/lexical_cast_test.cpp @@ -42,6 +42,10 @@ #define LCAST_TEST_LONGLONG #endif +#if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING) +#define BOOST_LCAST_NO_WCHAR_T +#endif + template struct my_traits : std::char_traits { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9517c63..2dd3500 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -27,6 +27,7 @@ test-suite conversion [ run lexical_cast_vc8_bug_test.cpp ../../test/build//boost_unit_test_framework/static ] [ run lexical_cast_wchars_test.cpp ../../test/build//boost_unit_test_framework/static ] [ run lexical_cast_float_types_test.cpp ../../test/build//boost_unit_test_framework/static ] + [ run lexical_cast_inf_nan_test.cpp ../../test/build//boost_unit_test_framework/static ] ; diff --git a/test/lexical_cast_float_types_test.cpp b/test/lexical_cast_float_types_test.cpp index 10074d4..72279bb 100755 --- a/test/lexical_cast_float_types_test.cpp +++ b/test/lexical_cast_float_types_test.cpp @@ -242,22 +242,6 @@ void test_converion_to_float_types() CHECK_CLOSE_ABS_DIFF(.34, test_t); CHECK_CLOSE_ABS_DIFF(.34e10, test_t); -// BOOST_CHECK(lexical_cast("-inf") == -std::numeric_limits::infinity() ); -// BOOST_CHECK(lexical_cast("-INF") == -std::numeric_limits::infinity() ); -// BOOST_CHECK(lexical_cast("+inf") == std::numeric_limits::infinity() ); -// BOOST_CHECK(lexical_cast("infinity") == std::numeric_limits::infinity() ); -// -// BOOST_CHECK(lexical_cast("nan") == std::numeric_limits::quiet_NaN() ); -// BOOST_CHECK(lexical_cast("NaN") == std::numeric_limits::quiet_NaN() ); - - BOOST_CHECK_THROW(lexical_cast("-inf"), bad_lexical_cast ); - BOOST_CHECK_THROW(lexical_cast("-INF"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("+inf"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("infinity"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("nan"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("NaN"), bad_lexical_cast); - - BOOST_CHECK_THROW(lexical_cast("-1.e"), bad_lexical_cast); BOOST_CHECK_THROW(lexical_cast("-1.E"), bad_lexical_cast); BOOST_CHECK_THROW(lexical_cast("1.e"), bad_lexical_cast); @@ -381,10 +365,6 @@ void test_converion_from_to_float_types() TEST_TO_FROM_CAST_AROUND( 0.0 ); -// TEST_TO_FROM_CAST_AROUND( std::numeric_limits::infinity() ); -// TEST_TO_FROM_CAST_AROUND( -std::numeric_limits::infinity() ); -// TEST_TO_FROM_CAST_AROUND( std::numeric_limits::quiet_NaN() ); - long double val1; for(val1 = 1.0e-10L; val1 < 1e11; val1*=10 ) TEST_TO_FROM_CAST_AROUND( val1 ); diff --git a/test/lexical_cast_inf_nan_test.cpp b/test/lexical_cast_inf_nan_test.cpp new file mode 100755 index 0000000..64a4957 --- /dev/null +++ b/test/lexical_cast_inf_nan_test.cpp @@ -0,0 +1,109 @@ +// Unit test for boost::lexical_cast. +// +// See http://www.boost.org for most recent version, including documentation. +// +// Copyright Antony Polukhin, 2011. +// +// Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). + +#include + +#if defined(__INTEL_COMPILER) +#pragma warning(disable: 193 383 488 981 1418 1419) +#elif defined(BOOST_MSVC) +#pragma warning(disable: 4097 4100 4121 4127 4146 4244 4245 4511 4512 4701 4800) +#endif + +#include + +#include +#include +#include + +#if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING) +#define BOOST_LCAST_NO_WCHAR_T +#endif + +using namespace boost; + +template +void test_inf_nan_templated() +{ + typedef T test_t; + + BOOST_CHECK(lexical_cast("inf") > (std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast("INF") > (std::numeric_limits::max)() ); + + BOOST_CHECK(lexical_cast("-inf") < -(std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast("-INF") < -(std::numeric_limits::max)() ); + + BOOST_CHECK(lexical_cast("+inf") > (std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast("+INF") > (std::numeric_limits::max)() ); + + BOOST_CHECK(lexical_cast("infinity") > (std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast("INFINITY") > (std::numeric_limits::max)() ); + + BOOST_CHECK(lexical_cast("-infinity") < -(std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast("-INFINITY") < -(std::numeric_limits::max)() ); + + BOOST_CHECK(lexical_cast("+infinity") > (std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast("+INFINITY") > (std::numeric_limits::max)() ); + + BOOST_CHECK( lexical_cast("nan") != lexical_cast("nan") ); + BOOST_CHECK( lexical_cast("NAN") != lexical_cast("NAN") ); + + BOOST_CHECK( lexical_cast("-nan") != lexical_cast("-nan") ); + BOOST_CHECK( lexical_cast("-NAN") != lexical_cast("-NAN") ); + + BOOST_CHECK( lexical_cast("+nan") != lexical_cast("+nan") ); + BOOST_CHECK( lexical_cast("+NAN") != lexical_cast("+NAN") ); + + BOOST_CHECK( lexical_cast("nan()") != lexical_cast("nan()") ); + BOOST_CHECK( lexical_cast("NAN(some string)") != lexical_cast("NAN(some string)") ); + BOOST_CHECK_THROW( lexical_cast("NAN(some string"), bad_lexical_cast ); + + BOOST_CHECK(lexical_cast( -std::numeric_limits::infinity()) == "-inf" ); + BOOST_CHECK(lexical_cast( std::numeric_limits::infinity()) == "inf" ); + BOOST_CHECK(lexical_cast( std::numeric_limits::quiet_NaN()) == "nan" ); + +#ifndef BOOST_LCAST_NO_WCHAR_T + BOOST_CHECK(lexical_cast(L"inf") > (std::numeric_limits::max)() ); + BOOST_CHECK(lexical_cast(L"INF") > (std::numeric_limits::max)() ); + + BOOST_CHECK( lexical_cast(L"nan") != lexical_cast(L"nan") ); + BOOST_CHECK( lexical_cast(L"NAN") != lexical_cast(L"NAN") ); + + BOOST_CHECK(lexical_cast( -std::numeric_limits::infinity()) == L"-inf" ); + BOOST_CHECK(lexical_cast( std::numeric_limits::infinity()) == L"inf" ); + BOOST_CHECK(lexical_cast( std::numeric_limits::quiet_NaN()) == L"nan" ); + +#endif +} + +void test_inf_nan_float() +{ + test_inf_nan_templated(); +} + +void test_inf_nan_double() +{ + test_inf_nan_templated(); +} + +void test_inf_nan_long_double() +{ + test_inf_nan_templated(); +} + +unit_test::test_suite *init_unit_test_suite(int, char *[]) +{ + unit_test_framework::test_suite *suite = + BOOST_TEST_SUITE("lexical_cast inf anf nan parsing unit test"); + suite->add(BOOST_TEST_CASE(&test_inf_nan_float)); + suite->add(BOOST_TEST_CASE(&test_inf_nan_double)); + suite->add(BOOST_TEST_CASE(&test_inf_nan_long_double)); + + return suite; +}