Fixes #5689. Added code to work with Inf and NaN on any platform

[SVN r73118]
This commit is contained in:
Antony Polukhin
2011-07-15 11:20:11 +00:00
parent 27a7fac22e
commit de3df54f80
6 changed files with 254 additions and 25 deletions

View File

@@ -24,6 +24,7 @@
#include <cstddef> #include <cstddef>
#include <istream> #include <istream>
#include <string> #include <string>
#include <cstring>
#include <typeinfo> #include <typeinfo>
#include <exception> #include <exception>
#include <cmath> #include <cmath>
@@ -731,6 +732,136 @@ namespace boost
} }
} }
namespace detail
{
/* Returns true and sets the correct value if found NaN or Inf. */
template <class CharT, class T>
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<wchar_t>::minus;
const wchar_t plus = lcast_char_constants<wchar_t>::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<T>::quiet_NaN();
else value = -std::numeric_limits<T>::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<T>::infinity();
else value = -std::numeric_limits<T>::infinity();
return true;
}
return false;
}
#ifndef BOOST_LCAST_NO_WCHAR_T
template <class T>
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 <class CharT, class T>
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 <class T>
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<T>::max() )
{
memcpy(begin,L"inf", sizeof(L"inf"));
end = begin + 3;
return true;
} else if ( value < -numeric_limits<T>::max() )
{
memcpy(begin,L"-inf", sizeof(L"-inf"));
end = begin + 4;
return true;
}
return false;
}
template <class CharT, class T>
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<T>::max() )
{
memcpy(begin,"inf", sizeof("inf"));
end = begin + 3;
return true;
} else if ( value < -numeric_limits<T>::max() )
{
memcpy(begin,"-inf", sizeof("-inf"));
end = begin + 4;
return true;
}
return false;
}
}
namespace detail // lcast_ret_float namespace detail // lcast_ret_float
{ {
template <class T> template <class T>
@@ -782,6 +913,8 @@ namespace boost
value = 0.0; value = 0.0;
if (parse_inf_nan(begin, end, value)) return true;
typedef typename Traits::int_type int_type; typedef typename Traits::int_type int_type;
typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type<T>::type mantissa_type; typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type<T>::type mantissa_type;
int_type const zero = Traits::to_int_type(czero); int_type const zero = Traits::to_int_type(czero);
@@ -1104,6 +1237,7 @@ namespace boost
template<class OutputStreamable> template<class OutputStreamable>
bool lcast_put(const OutputStreamable& input) bool lcast_put(const OutputStreamable& input)
{ {
if(put_inf_nan(start, finish, input)) return true;
this->setp(start, finish); this->setp(start, finish);
std::basic_ostream<CharT> stream(static_cast<Base*>(this)); std::basic_ostream<CharT> stream(static_cast<Base*>(this));
lcast_set_precision(stream, static_cast<OutputStreamable*>(0)); lcast_set_precision(stream, static_cast<OutputStreamable*>(0));
@@ -1342,10 +1476,13 @@ namespace boost
// Not optimised converter // Not optimised converter
template <class T> template <class T>
bool float_types_converter_internal(T& output, int /*tag*/) { 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); bool return_value = convert_using_base_class(output);
/* Some compilers and libraries successfully /* 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, * We are trying to provide a unified behaviour,
* so we just forbid such conversions (as some * so we just forbid such conversions (as some
* of the most popular compilers/libraries do) * of the most popular compilers/libraries do)
@@ -1356,10 +1493,7 @@ namespace boost
CharT const lowercase_e = lcast_char_constants<CharT>::lowercase_e; CharT const lowercase_e = lcast_char_constants<CharT>::lowercase_e;
if ( return_value && if ( return_value &&
( (
output > (std::numeric_limits<T>::max)() // +inf *(finish-1) == lowercase_e // 1.0e
|| output < -(std::numeric_limits<T>::max)() // -inf
|| output != output // NaN
|| *(finish-1) == lowercase_e // 1.0e
|| *(finish-1) == capital_e // 1.0E || *(finish-1) == capital_e // 1.0E
|| *(finish-1) == minus // 1.0e- or 1.0E- || *(finish-1) == minus // 1.0e- or 1.0E-
|| *(finish-1) == plus // 1.0e+ or 1.0E+ || *(finish-1) == plus // 1.0e+ or 1.0E+

View File

@@ -269,6 +269,7 @@ Eliminate an overhead of <code>std::locale</code> if your program runs in the "C
<h2><a name="changes">Changes</a></h2> <h2><a name="changes">Changes</a></h2>
<h3>July 2011:</h3> <h3>July 2011:</h3>
<ul type="square"> <ul type="square">
<li>Added code to work with Inf and NaN on any platform.</li>
<li>Better performance and less memory usage for conversions to float type (and to double type, if sizeof(double)&lt;sizeof(long double)).</li> <li>Better performance and less memory usage for conversions to float type (and to double type, if sizeof(double)&lt;sizeof(long double)).</li>
</ul> </ul>
<h3>May 2011:</h3> <h3>May 2011:</h3>

View File

@@ -42,6 +42,10 @@
#define LCAST_TEST_LONGLONG #define LCAST_TEST_LONGLONG
#endif #endif
#if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING)
#define BOOST_LCAST_NO_WCHAR_T
#endif
template<class CharT> template<class CharT>
struct my_traits : std::char_traits<CharT> struct my_traits : std::char_traits<CharT>
{ {

View File

@@ -27,6 +27,7 @@ test-suite conversion
[ run lexical_cast_vc8_bug_test.cpp ../../test/build//boost_unit_test_framework/<link>static ] [ run lexical_cast_vc8_bug_test.cpp ../../test/build//boost_unit_test_framework/<link>static ]
[ run lexical_cast_wchars_test.cpp ../../test/build//boost_unit_test_framework/<link>static ] [ run lexical_cast_wchars_test.cpp ../../test/build//boost_unit_test_framework/<link>static ]
[ run lexical_cast_float_types_test.cpp ../../test/build//boost_unit_test_framework/<link>static ] [ run lexical_cast_float_types_test.cpp ../../test/build//boost_unit_test_framework/<link>static ]
[ run lexical_cast_inf_nan_test.cpp ../../test/build//boost_unit_test_framework/<link>static ]
; ;

View File

@@ -242,22 +242,6 @@ void test_converion_to_float_types()
CHECK_CLOSE_ABS_DIFF(.34, test_t); CHECK_CLOSE_ABS_DIFF(.34, test_t);
CHECK_CLOSE_ABS_DIFF(.34e10, test_t); CHECK_CLOSE_ABS_DIFF(.34e10, test_t);
// BOOST_CHECK(lexical_cast<test_t>("-inf") == -std::numeric_limits<test_t>::infinity() );
// BOOST_CHECK(lexical_cast<test_t>("-INF") == -std::numeric_limits<test_t>::infinity() );
// BOOST_CHECK(lexical_cast<test_t>("+inf") == std::numeric_limits<test_t>::infinity() );
// BOOST_CHECK(lexical_cast<test_t>("infinity") == std::numeric_limits<test_t>::infinity() );
//
// BOOST_CHECK(lexical_cast<test_t>("nan") == std::numeric_limits<test_t>::quiet_NaN() );
// BOOST_CHECK(lexical_cast<test_t>("NaN") == std::numeric_limits<test_t>::quiet_NaN() );
BOOST_CHECK_THROW(lexical_cast<test_t>("-inf"), bad_lexical_cast );
BOOST_CHECK_THROW(lexical_cast<test_t>("-INF"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("+inf"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("infinity"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("nan"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("NaN"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("-1.e"), bad_lexical_cast); BOOST_CHECK_THROW(lexical_cast<test_t>("-1.e"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("-1.E"), bad_lexical_cast); BOOST_CHECK_THROW(lexical_cast<test_t>("-1.E"), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<test_t>("1.e"), bad_lexical_cast); BOOST_CHECK_THROW(lexical_cast<test_t>("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( 0.0 );
// TEST_TO_FROM_CAST_AROUND( std::numeric_limits<test_t>::infinity() );
// TEST_TO_FROM_CAST_AROUND( -std::numeric_limits<test_t>::infinity() );
// TEST_TO_FROM_CAST_AROUND( std::numeric_limits<test_t>::quiet_NaN() );
long double val1; long double val1;
for(val1 = 1.0e-10L; val1 < 1e11; val1*=10 ) for(val1 = 1.0e-10L; val1 < 1e11; val1*=10 )
TEST_TO_FROM_CAST_AROUND( val1 ); TEST_TO_FROM_CAST_AROUND( val1 );

View File

@@ -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 <boost/config.hpp>
#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 <boost/lexical_cast.hpp>
#include <boost/cstdint.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING)
#define BOOST_LCAST_NO_WCHAR_T
#endif
using namespace boost;
template <class T>
void test_inf_nan_templated()
{
typedef T test_t;
BOOST_CHECK(lexical_cast<test_t>("inf") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("INF") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("-inf") < -(std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("-INF") < -(std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("+inf") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("+INF") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("infinity") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("INFINITY") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("-infinity") < -(std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("-INFINITY") < -(std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("+infinity") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>("+INFINITY") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK( lexical_cast<test_t>("nan") != lexical_cast<test_t>("nan") );
BOOST_CHECK( lexical_cast<test_t>("NAN") != lexical_cast<test_t>("NAN") );
BOOST_CHECK( lexical_cast<test_t>("-nan") != lexical_cast<test_t>("-nan") );
BOOST_CHECK( lexical_cast<test_t>("-NAN") != lexical_cast<test_t>("-NAN") );
BOOST_CHECK( lexical_cast<test_t>("+nan") != lexical_cast<test_t>("+nan") );
BOOST_CHECK( lexical_cast<test_t>("+NAN") != lexical_cast<test_t>("+NAN") );
BOOST_CHECK( lexical_cast<test_t>("nan()") != lexical_cast<test_t>("nan()") );
BOOST_CHECK( lexical_cast<test_t>("NAN(some string)") != lexical_cast<test_t>("NAN(some string)") );
BOOST_CHECK_THROW( lexical_cast<test_t>("NAN(some string"), bad_lexical_cast );
BOOST_CHECK(lexical_cast<std::string>( -std::numeric_limits<test_t >::infinity()) == "-inf" );
BOOST_CHECK(lexical_cast<std::string>( std::numeric_limits<test_t >::infinity()) == "inf" );
BOOST_CHECK(lexical_cast<std::string>( std::numeric_limits<test_t >::quiet_NaN()) == "nan" );
#ifndef BOOST_LCAST_NO_WCHAR_T
BOOST_CHECK(lexical_cast<test_t>(L"inf") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK(lexical_cast<test_t>(L"INF") > (std::numeric_limits<test_t >::max)() );
BOOST_CHECK( lexical_cast<test_t>(L"nan") != lexical_cast<test_t>(L"nan") );
BOOST_CHECK( lexical_cast<test_t>(L"NAN") != lexical_cast<test_t>(L"NAN") );
BOOST_CHECK(lexical_cast<std::wstring>( -std::numeric_limits<test_t >::infinity()) == L"-inf" );
BOOST_CHECK(lexical_cast<std::wstring>( std::numeric_limits<test_t >::infinity()) == L"inf" );
BOOST_CHECK(lexical_cast<std::wstring>( std::numeric_limits<test_t >::quiet_NaN()) == L"nan" );
#endif
}
void test_inf_nan_float()
{
test_inf_nan_templated<float >();
}
void test_inf_nan_double()
{
test_inf_nan_templated<double >();
}
void test_inf_nan_long_double()
{
test_inf_nan_templated<long double >();
}
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;
}