diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index ddd7398..d9b73af 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -1,6 +1,12 @@ #ifndef BOOST_LEXICAL_CAST_INCLUDED #define BOOST_LEXICAL_CAST_INCLUDED +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + // Boost lexical_cast.hpp header -------------------------------------------// // // See http://www.boost.org/libs/conversion for documentation. @@ -502,6 +508,9 @@ namespace boost BOOST_STATIC_CONSTANT(char, zero = '0'); BOOST_STATIC_CONSTANT(char, minus = '-'); BOOST_STATIC_CONSTANT(char, plus = '+'); + BOOST_STATIC_CONSTANT(char, lowercase_e = 'e'); + BOOST_STATIC_CONSTANT(char, capital_e = 'E'); + BOOST_STATIC_CONSTANT(char, c_decimal_separator = '.'); }; #ifndef BOOST_LCAST_NO_WCHAR_T @@ -511,6 +520,9 @@ namespace boost BOOST_STATIC_CONSTANT(wchar_t, zero = L'0'); BOOST_STATIC_CONSTANT(wchar_t, minus = L'-'); BOOST_STATIC_CONSTANT(wchar_t, plus = L'+'); + BOOST_STATIC_CONSTANT(wchar_t, lowercase_e = L'e'); + BOOST_STATIC_CONSTANT(wchar_t, capital_e = L'E'); + BOOST_STATIC_CONSTANT(wchar_t, c_decimal_separator = L'.'); }; #endif } @@ -646,7 +658,7 @@ namespace boost std::string const& grouping = np.grouping(); std::string::size_type const grouping_size = grouping.size(); - /* According to [22.2.2.1.2] of Programming languages - C++ + /* According to Programming languages - C++ * we MUST check for correct grouping */ if (grouping_size && grouping[0] > 0) @@ -719,6 +731,246 @@ namespace boost } } + namespace detail // lcast_ret_float + { + template + struct mantissa_holder_type + { + /* Can not be used with this type */ + }; + + template <> + struct mantissa_holder_type + { + typedef unsigned int type; + }; + + template <> + struct mantissa_holder_type + { +#if defined(BOOST_HAS_LONG_LONG) + typedef boost::ulong_long_type type; +#elif defined(BOOST_HAS_MS_INT64) + typedef unsigned __int64 type; +#endif + }; + + template + inline bool lcast_ret_float(T& value, const CharT* begin, const CharT* end) + { + +#ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE + // TODO: use BOOST_NO_STD_LOCALE + std::locale loc; + typedef std::numpunct numpunct; + numpunct const& np = BOOST_USE_FACET(numpunct, loc); + std::string const& grouping = np.grouping(); + std::string::size_type const grouping_size = grouping.size(); + CharT const thousands_sep = grouping_size ? np.thousands_sep() : 0; + CharT const decimal_point = np.decimal_point(); + bool found_grouping = false; + unsigned int last_grouping_pos = grouping_size - 1; +#else + CharT const decimal_point = lcast_char_constants::c_decimal_separator; +#endif + + CharT const czero = lcast_char_constants::zero; + CharT const minus = lcast_char_constants::minus; + CharT const plus = lcast_char_constants::plus; + CharT const capital_e = lcast_char_constants::capital_e; + CharT const lowercase_e = lcast_char_constants::lowercase_e; + + value = 0.0; + + 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); + if (begin == end) return false; + + /* Getting the plus/minus sign */ + bool has_minus = false; + if ( *begin == minus ) { + ++ begin; + has_minus = true; + if (begin == end) return false; + } else if ( *begin == plus ) { + ++begin; + if (begin == end) return false; + } + + if ( *begin < czero || *begin >= czero + 10 ) { + return false; + } + + + bool found_decimal = false; + int pow_of_10 = 0; + mantissa_type mantissa=0; + bool is_mantissa_full = false; + + char length_since_last_delim = 0; + + while ( begin != end ) + { + if (found_decimal) { + /* We allow no thousand_separators after decimal point */ + + mantissa_type tmp_mantissa = mantissa * 10u; + if ( *begin == lowercase_e || *begin == capital_e ) break; + if ( *begin < czero || *begin >= czero + 10 ) return false; + if ( is_mantissa_full + || tmp_mantissa / 10u != mantissa + || (std::numeric_limits::max)()-(*begin - zero) < tmp_mantissa + ) { + is_mantissa_full = true; + ++ begin; + continue; + } + + -- pow_of_10; + mantissa = tmp_mantissa; + mantissa += *begin - zero; + } else { + + if (*begin >= czero && *begin < czero + 10) { + + /* Checking for mantissa overflow. If overflow will + * occur, them we only increase multiplyer + */ + mantissa_type tmp_mantissa = mantissa * 10u; + if( !is_mantissa_full + && tmp_mantissa / 10u == mantissa + && (std::numeric_limits::max)()-(*begin - zero) >= tmp_mantissa + ) + { + mantissa = tmp_mantissa; + mantissa += *begin - zero; + } else + { + is_mantissa_full = true; + ++ pow_of_10; + } + + ++ length_since_last_delim; + } else if ( *begin == decimal_point || *begin == lowercase_e || *begin == capital_e) { +#ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE + /* If ( we need to check grouping + * and ( grouping missmatches + * or grouping position is incorrect + * or we are using the grouping position 0 twice + * ) + * ) then return error + */ + if( grouping_size && found_grouping + && ( + length_since_last_delim != grouping[0] + || last_grouping_pos>1 + || (last_grouping_pos==0 && grouping_size>1) + ) + ) return false; +#endif + + if(*begin == decimal_point){ + ++ begin; + found_decimal = true; + continue; + }else break; + } +#ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE + else if (grouping_size && *begin == thousands_sep){ + if(found_grouping) + { + /* It is not he first time, when we find thousands separator, + * so we need to chek, is the distance between two groupings + * equal to grouping[last_grouping_pos] */ + + if (length_since_last_delim != grouping[last_grouping_pos] ) + { + if (!last_grouping_pos) return false; + else + { + -- last_grouping_pos; + if (length_since_last_delim != grouping[last_grouping_pos]) return false; + } + } else + /* We are calling the grouping[0] twice, when grouping size is more than 1 */ + if (grouping_size>1u && last_grouping_pos+1 grouping[last_grouping_pos] ) return false; + } + + length_since_last_delim = 0; + ++ begin; + + /* Delimiter at the end '100,' */ + if (begin == end) return false; + continue; + } +#endif + else return false; + } + + ++begin; + } + + // Exponent found + if ( begin != end && ( *begin == lowercase_e || *begin == capital_e ) ) { + ++ begin; + if ( begin == end ) return false; + + bool exp_has_minus = false; + if( *begin == minus ) { + exp_has_minus = true; + ++ begin; + if ( begin == end ) return false; + } else if (*begin == plus ) { + ++ begin; + if ( begin == end ) return false; + } + + int exp_pow_of_10 = 0; + while ( begin != end ) + { + if ( *begin < czero + || *begin >= czero + 10 + || exp_pow_of_10 * 10 < exp_pow_of_10) /* Overflows are checked lower more precisely*/ + return false; + + exp_pow_of_10 *= 10; + exp_pow_of_10 += *begin - zero; + ++ begin; + }; + + if ( exp_pow_of_10 ) { + /* Overflows are checked lower */ + if ( exp_has_minus ) { + pow_of_10 -= exp_pow_of_10; + } else { + pow_of_10 += exp_pow_of_10; + } + } + } + + /* We need a more accurate algorithm... We can not use current algorithm + * with long doubles (and with doubles if sizeof(double)==sizeof(long double)). + */ + long double result = std::pow(10.0L, pow_of_10) * mantissa; + value = ( has_minus ? -1 * result : result); + + if ( value > (std::numeric_limits::max)() // is it +inf + || value < -(std::numeric_limits::max)() // is it -inf + || value != value) // is it NaN + return false; + + return true; + } + } + namespace detail // stream wrapper for handling lexical conversions { template @@ -1079,12 +1331,54 @@ namespace boost } } + bool operator>>(float& output) + { + return lcast_ret_float(output,start,finish); + } + +#if defined(BOOST_HAS_LONG_LONG) || defined(BOOST_HAS_MS_INT64) + private: + // we need workaround + bool no_long_double_80bit_realization_workaround(double& output, int) { + return convert_using_base_class(output); + } + + // we do not need a workaround + bool no_long_double_80bit_realization_workaround(double& output,char) { + return lcast_ret_float(output,start,finish); + } + public: + + bool operator>>(double& output) + { + /* + * Some compilers implement long double as double. In that case these types have + * same size, same precision, same max and min values... And it means, + * that current implementation of lcast_ret_float cannot be used for type + * double, because it will give a big precision loss. + * */ + boost::mpl::if_c< + ::boost::type_traits::ice_eq< sizeof(double), sizeof(long double) >::value, + int, + char + >::type dummy = 0; + + return no_long_double_80bit_realization_workaround(output, dummy); + } +#endif // Generic istream-based algorithm. // lcast_streambuf_for_target::value is true. template bool operator>>(InputStreamable& output) { + return convert_using_base_class(output); + } + + private: + template + bool convert_using_base_class(InputStreamable& output) + { #if (defined _MSC_VER) # pragma warning( push ) // conditional expression is constant @@ -1112,6 +1406,7 @@ namespace boost Traits::eof(); #endif } + public: bool operator>>(CharT&); bool operator>>(unsigned char&); @@ -1433,11 +1728,30 @@ namespace boost template struct lcast_streambuf_for_target { +#if defined(BOOST_HAS_LONG_LONG) || defined(BOOST_HAS_MS_INT64) BOOST_STATIC_CONSTANT(bool, value = ( - ::boost::type_traits::ice_not< is_integral::value >::value + ::boost::type_traits::ice_or< + ::boost::type_traits::ice_not< is_arithmetic::value >::value, + is_same::value, + ::boost::type_traits::ice_and< + is_same::value, + ::boost::type_traits::ice_eq::value + >::value + >::value ) ); +#else + BOOST_STATIC_CONSTANT(bool, value = + ( + ::boost::type_traits::ice_or< + ::boost::type_traits::ice_not< is_arithmetic::value >::value, + is_same::value, + is_same::value + >::value + ) + ); +#endif }; #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION diff --git a/lexical_cast.htm b/lexical_cast.htm index 7929dd4..7a1fee5 100644 --- a/lexical_cast.htm +++ b/lexical_cast.htm @@ -267,6 +267,10 @@ Eliminate an overhead of std::locale if your program runs in the "C August 2006.

Changes

+

July 2011:

+
    +
  • Better performance and less memory usage for conversions to float type (and to double type, if sizeof(double)<sizeof(long double)).
  • +

May 2011:

  • Optimizations for "C" and other locales without number grouping.
  • diff --git a/lexical_cast_test.cpp b/lexical_cast_test.cpp index f20994e..cdf42fe 100644 --- a/lexical_cast_test.cpp +++ b/lexical_cast_test.cpp @@ -86,9 +86,6 @@ void test_conversion_from_to_uintmax_t(); void test_conversion_from_to_longlong(); void test_conversion_from_to_ulonglong(); #endif -void test_conversion_from_to_float(); -void test_conversion_from_to_double(); -void test_conversion_from_to_long_double(); #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION void test_traits(); void test_wtraits(); @@ -128,9 +125,6 @@ unit_test::test_suite *init_unit_test_suite(int, char *[]) suite->add(BOOST_TEST_CASE(&test_conversion_from_to_longlong)); suite->add(BOOST_TEST_CASE(&test_conversion_from_to_ulonglong)); #endif - suite->add(BOOST_TEST_CASE(&test_conversion_from_to_float)); - suite->add(BOOST_TEST_CASE(&test_conversion_from_to_double)); - suite->add(BOOST_TEST_CASE(&test_conversion_from_to_long_double)); #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION suite->add(BOOST_TEST_CASE(&test_traits)); suite->add(BOOST_TEST_CASE(&test_wtraits)); @@ -679,7 +673,7 @@ void test_conversion_from_to_integral_for_locale() BOOST_CHECK_THROW(lexical_cast( np.thousands_sep() + std::string("100") ), bad_lexical_cast); // Exception must not be thrown, when we are using no separators at all - BOOST_CHECK( lexical_cast("10000") == static_cast(10000) ); + BOOST_CHECK( lexical_cast("30000") == static_cast(30000) ); } test_conversion_from_integral_to_integral(); @@ -775,35 +769,6 @@ void test_conversion_from_to_integral() BOOST_TEST_MESSAGE("Formatting with thousands_sep has not been tested"); } -template -void test_conversion_from_to_float() -{ - char const zero = '0'; - signed char const szero = '0'; - unsigned char const uzero = '0'; - test_conversion_from_integral_to_char(zero); - test_conversion_from_char_to_integral(zero); - test_conversion_from_integral_to_char(szero); - test_conversion_from_char_to_integral(szero); - test_conversion_from_integral_to_char(uzero); - test_conversion_from_char_to_integral(uzero); -#if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) - wchar_t const wzero = L'0'; - test_conversion_from_integral_to_char(wzero); - test_conversion_from_char_to_integral(wzero); -#endif - - test_conversion_from_integral_to_integral(); - - BOOST_CHECK_CLOSE(lexical_cast("+1"), 1, std::numeric_limits::epsilon() ); - BOOST_CHECK_CLOSE(lexical_cast("+9"), 9, std::numeric_limits::epsilon()*9 ); - - BOOST_CHECK_THROW(lexical_cast("++1"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("-+9"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("--1"), bad_lexical_cast); - BOOST_CHECK_THROW(lexical_cast("+-9"), bad_lexical_cast); -} - void test_conversion_from_to_short() { test_conversion_from_to_integral(); @@ -844,19 +809,6 @@ void test_conversion_from_to_uintmax_t() test_conversion_from_to_integral(); } -void test_conversion_from_to_float() -{ - test_conversion_from_to_float(); -} -void test_conversion_from_to_double() -{ - test_conversion_from_to_float(); -} -void test_conversion_from_to_long_double() -{ - test_conversion_from_to_float(); -} - #if defined(BOOST_HAS_LONG_LONG) void test_conversion_from_to_longlong() @@ -966,16 +918,25 @@ void test_char_types_conversions() const wchar_t wc_arr[]=L"Test array of chars"; BOOST_CHECK(boost::lexical_cast(wc_arr) == std::wstring(wc_arr)); + BOOST_CHECK(boost::lexical_cast(wc_arr[0]) == wc_arr[0]); + + // Following tests depend on realization of std::locale. + // All we need to know, is that this functions must compile + BOOST_CHECK(boost::lexical_cast(c_arr[0]) == wc_arr[0]); BOOST_CHECK(boost::lexical_cast(c_arr) == std::wstring(wc_arr)); BOOST_CHECK(boost::lexical_cast(sc_arr) != std::wstring(wc_arr) ); BOOST_CHECK(boost::lexical_cast(uc_arr) != std::wstring(wc_arr) ); - BOOST_CHECK(boost::lexical_cast(c_arr[0]) == wc_arr[0]); - BOOST_CHECK(boost::lexical_cast(wc_arr[0]) == wc_arr[0]); - BOOST_CHECK_THROW(boost::lexical_cast(uc_arr[0]), bad_lexical_cast); BOOST_CHECK_THROW(boost::lexical_cast(sc_arr[0]), bad_lexical_cast); #endif } + + + + + + + diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 1fea2fb..9517c63 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -25,6 +25,8 @@ test-suite conversion [ run lexical_cast_abstract_test.cpp ../../test/build//boost_unit_test_framework/static ] [ run lexical_cast_noncopyable_test.cpp ../../test/build//boost_unit_test_framework/static ] [ 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 ] ; diff --git a/test/lexical_cast_float_types_test.cpp b/test/lexical_cast_float_types_test.cpp new file mode 100755 index 0000000..442ed3b --- /dev/null +++ b/test/lexical_cast_float_types_test.cpp @@ -0,0 +1,527 @@ +// 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 + +void test_conversion_from_to_float(); +void test_conversion_from_to_double(); +void test_conversion_from_to_long_double(); + +using namespace boost; + + +unit_test::test_suite *init_unit_test_suite(int, char *[]) +{ + unit_test_framework::test_suite *suite = + BOOST_TEST_SUITE("lexical_cast float types unit test"); + suite->add(BOOST_TEST_CASE(&test_conversion_from_to_float)); + suite->add(BOOST_TEST_CASE(&test_conversion_from_to_double)); + suite->add(BOOST_TEST_CASE(&test_conversion_from_to_long_double)); + + return suite; +} + + +// Replace "-,999" with "-999". +template +std::basic_string to_str_gcc_workaround(std::basic_string str) +{ + std::locale loc; + std::numpunct const& np = BOOST_USE_FACET(std::numpunct, loc); + std::ctype const& ct = BOOST_USE_FACET(std::ctype, loc); + + if(np.grouping().empty()) + return str; + + CharT prefix[3] = { ct.widen('-'), np.thousands_sep(), CharT() }; + + if(str.find(prefix) != 0) + return str; + + prefix[1] = CharT(); + str.replace(0, 2, prefix); + return str; +} + +template +std::basic_string to_str(T t) +{ + std::basic_ostringstream o; + o << t; + return to_str_gcc_workaround(o.str()); +} + + +template +void test_conversion_from_to_float_for_locale() +{ + std::locale current_locale; + typedef std::numpunct numpunct; + numpunct const& np = BOOST_USE_FACET(numpunct, current_locale); + if ( !np.grouping().empty() ) + { + BOOST_CHECK_THROW( + lexical_cast( std::string("100") + np.thousands_sep() + np.thousands_sep() + "0" ) + , bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( std::string("100") + np.thousands_sep() ), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( np.thousands_sep() + std::string("100") ), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( std::string("1") + np.thousands_sep() + np.decimal_point() + "e10" ), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( std::string("1e10") + np.thousands_sep() ), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( std::string("1") + np.thousands_sep() + "e10" ), bad_lexical_cast); + + BOOST_CHECK_CLOSE(lexical_cast( to_str< char >(100000) ), 100000, (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( to_str< char >(10000000u) ), 10000000u, (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( to_str< char >(100) ), 100, (std::numeric_limits::epsilon()*100) ); +#if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) + BOOST_CHECK_CLOSE(lexical_cast( to_str< wchar_t >(100000) ), 100000, (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( to_str< wchar_t >(10000000u) ), 10000000u, (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( to_str< wchar_t >(100) ), 100, (std::numeric_limits::epsilon()*100) ); +#endif + // Exception must not be thrown, when we are using no separators at all + BOOST_CHECK_CLOSE( lexical_cast("30000"), static_cast(30000), (std::numeric_limits::epsilon()*100) ); + } +} + + + + +/* + * Converts char* [and wchar_t] to float number type and checks, that generated + * number is in interval [base_value-epsilon, base_value+epsilon]. + */ +#ifndef BOOST_LCAST_NO_WCHAR_T +#define CHECK_CLOSE_ABS_DIFF(VAL,PREFIX) \ + converted_val = lexical_cast(#VAL); \ + BOOST_CHECK_CLOSE( (VAL ## L? VAL ## L : std::numeric_limits::epsilon()), \ + (converted_val ? converted_val : std::numeric_limits::epsilon()), \ + std::numeric_limits::epsilon() * 100 \ + ); \ + BOOST_CHECK_EQUAL(converted_val, lexical_cast(L## #VAL) ); + +#else +#define CHECK_CLOSE_ABS_DIFF(VAL,TYPE) \ + converted_val = lexical_cast(#VAL); \ + BOOST_CHECK_CLOSE( (VAL ## L? VAL ## L : std::numeric_limits::epsilon()), \ + (converted_val ? converted_val : std::numeric_limits::epsilon()), \ + std::numeric_limits::epsilon() * 100 \ + ); +#endif + +template +void test_converion_to_float_types() +{ + typedef TestType test_t; + test_t converted_val; + + BOOST_CHECK_CLOSE(1.0, lexical_cast('1'), (std::numeric_limits::epsilon())); + BOOST_CHECK_EQUAL(0.0, lexical_cast('0')); + + unsigned char const uc_one = '1'; + unsigned char const uc_zero ='0'; + BOOST_CHECK_CLOSE(1.0, lexical_cast(uc_one), (std::numeric_limits::epsilon())); + BOOST_CHECK_EQUAL(0.0, lexical_cast(uc_zero)); + + signed char const sc_one = '1'; + signed char const sc_zero ='0'; + BOOST_CHECK_CLOSE(1.0, lexical_cast(sc_one), (std::numeric_limits::epsilon())); + BOOST_CHECK_EQUAL(0.0, lexical_cast(sc_zero)); + + BOOST_CHECK_CLOSE(1e34L, lexical_cast( "10000000000000000000000000000000000"), (std::numeric_limits::epsilon()*100) ); + +// VC failes the next test +// BOOST_CHECK_CLOSE(1e-35L, lexical_cast("0.00000000000000000000000000000000001"), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE( + 0.1111111111111111111111111111111111111111111111111111111111111111111111111L + , lexical_cast("0.1111111111111111111111111111111111111111111111111111111111111111111111111") + , (std::numeric_limits::epsilon()*100) ); + + CHECK_CLOSE_ABS_DIFF(1,test_t); + BOOST_CHECK_EQUAL(0,lexical_cast("0")); + CHECK_CLOSE_ABS_DIFF(-1,test_t); + + CHECK_CLOSE_ABS_DIFF(1.0, test_t); + CHECK_CLOSE_ABS_DIFF(0.0, test_t); + CHECK_CLOSE_ABS_DIFF(-1.0,test_t); + + CHECK_CLOSE_ABS_DIFF(1e1, test_t); + CHECK_CLOSE_ABS_DIFF(0e1, test_t); + CHECK_CLOSE_ABS_DIFF(-1e1,test_t); + + CHECK_CLOSE_ABS_DIFF(1.0e1, test_t); + CHECK_CLOSE_ABS_DIFF(0.0e1, test_t); + CHECK_CLOSE_ABS_DIFF(-1.0e1,test_t); + + CHECK_CLOSE_ABS_DIFF(1e-1, test_t); + CHECK_CLOSE_ABS_DIFF(0e-1, test_t); + CHECK_CLOSE_ABS_DIFF(-1e-1,test_t); + + CHECK_CLOSE_ABS_DIFF(1.0e-1, test_t); + CHECK_CLOSE_ABS_DIFF(0.0e-1, test_t); + CHECK_CLOSE_ABS_DIFF(-1.0e-1,test_t); + + CHECK_CLOSE_ABS_DIFF(1E1, test_t); + CHECK_CLOSE_ABS_DIFF(0E1, test_t); + CHECK_CLOSE_ABS_DIFF(-1E1,test_t); + + CHECK_CLOSE_ABS_DIFF(1.0E1, test_t); + CHECK_CLOSE_ABS_DIFF(0.0E1, test_t); + CHECK_CLOSE_ABS_DIFF(-1.0E1,test_t); + + CHECK_CLOSE_ABS_DIFF(1E-1, test_t); + CHECK_CLOSE_ABS_DIFF(0E-1, test_t); + CHECK_CLOSE_ABS_DIFF(-1E-1,test_t); + + CHECK_CLOSE_ABS_DIFF(1.0E-1, test_t); + CHECK_CLOSE_ABS_DIFF(0.0E-1, test_t); + CHECK_CLOSE_ABS_DIFF(-1.0E-1, test_t); + + + + CHECK_CLOSE_ABS_DIFF(10.0, test_t); + CHECK_CLOSE_ABS_DIFF(00.0, test_t); + CHECK_CLOSE_ABS_DIFF(-10.0,test_t); + + CHECK_CLOSE_ABS_DIFF(10e1, test_t); + CHECK_CLOSE_ABS_DIFF(00e1, test_t); + CHECK_CLOSE_ABS_DIFF(-10e1,test_t); + + CHECK_CLOSE_ABS_DIFF(10.0e1, test_t); + CHECK_CLOSE_ABS_DIFF(00.0e1, test_t); + CHECK_CLOSE_ABS_DIFF(-10.0e1,test_t); + + CHECK_CLOSE_ABS_DIFF(10e-1, test_t); + CHECK_CLOSE_ABS_DIFF(00e-1, test_t); + CHECK_CLOSE_ABS_DIFF(-10e-1,test_t); + + CHECK_CLOSE_ABS_DIFF(10.0e-1, test_t); + CHECK_CLOSE_ABS_DIFF(00.0e-1, test_t); + CHECK_CLOSE_ABS_DIFF(-10.0e-1,test_t); + + CHECK_CLOSE_ABS_DIFF(10E1, test_t); + CHECK_CLOSE_ABS_DIFF(00E1, test_t); + CHECK_CLOSE_ABS_DIFF(-10E1,test_t); + + CHECK_CLOSE_ABS_DIFF(10.0E1, test_t); + CHECK_CLOSE_ABS_DIFF(00.0E1, test_t); + CHECK_CLOSE_ABS_DIFF(-10.0E1,test_t); + + CHECK_CLOSE_ABS_DIFF(10E-1, test_t); + CHECK_CLOSE_ABS_DIFF(00E-1, test_t); + CHECK_CLOSE_ABS_DIFF(-10E-1,test_t); + + CHECK_CLOSE_ABS_DIFF(10.0E-1, test_t); + CHECK_CLOSE_ABS_DIFF(00.0E-1, test_t); + CHECK_CLOSE_ABS_DIFF(-10.0E-1, test_t); + + CHECK_CLOSE_ABS_DIFF(-10101.0E-011, test_t); + CHECK_CLOSE_ABS_DIFF(-10101093, test_t); + CHECK_CLOSE_ABS_DIFF(10101093, 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); + BOOST_CHECK_THROW(lexical_cast("1.E"), bad_lexical_cast); + + BOOST_CHECK_THROW(lexical_cast("1.0e"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0E"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("10E"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("10e"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0e-"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0E-"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("10E-"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("10e-"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("e1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("e-1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("e-"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(".e"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(".11111111111111111111111111111111111111111111111111111111111111111111ee"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(".11111111111111111111111111111111111111111111111111111111111111111111e-"), bad_lexical_cast); + + BOOST_CHECK_THROW(lexical_cast("-B"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("0xB"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("0x0"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("--1.0"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0e--1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0.0"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1e1e1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0e-1e-1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(" 1.0"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("1.0 "), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(""), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("-"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast('\0'), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast('-'), bad_lexical_cast); +} + +template +void test_float_typess_for_overflows() +{ + typedef T test_t; + test_t minvalue = (std::numeric_limits::min)(); + std::string s_min_value = lexical_cast(minvalue); + BOOST_CHECK_CLOSE(minvalue, lexical_cast(minvalue), (std::numeric_limits::epsilon()*100)); + BOOST_CHECK_CLOSE(minvalue, lexical_cast(s_min_value), (std::numeric_limits::epsilon()*100)); + + test_t maxvalue = (std::numeric_limits::max)(); + std::string s_max_value = lexical_cast(maxvalue); + BOOST_CHECK_CLOSE(maxvalue, lexical_cast(maxvalue), (std::numeric_limits::epsilon()*100)); + BOOST_CHECK_CLOSE(maxvalue, lexical_cast(s_max_value), (std::numeric_limits::epsilon()*100)); + + BOOST_CHECK_THROW(lexical_cast(s_max_value+"1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(s_max_value+"9"), bad_lexical_cast); + + // VC9 can fail the fllowing tests on floats and doubles when using stingstream... + BOOST_CHECK_THROW(lexical_cast("1"+s_max_value), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("9"+s_max_value), bad_lexical_cast); + + if ( is_same::value ) + { + BOOST_CHECK_THROW(lexical_cast( (std::numeric_limits::max)() ), bad_lexical_cast); + BOOST_CHECK( + (std::numeric_limits::min)() - std::numeric_limits::epsilon() + <= lexical_cast( (std::numeric_limits::min)() ) + && lexical_cast( (std::numeric_limits::min)() ) + <= (std::numeric_limits::min)() + std::numeric_limits::epsilon() + ); + } + + if ( sizeof(test_t) < sizeof(long double) ) + { + BOOST_CHECK_THROW(lexical_cast( (std::numeric_limits::max)() ), bad_lexical_cast); + BOOST_CHECK( + (std::numeric_limits::min)() - std::numeric_limits::epsilon() + <= lexical_cast( (std::numeric_limits::min)() ) + && lexical_cast( (std::numeric_limits::min)() ) + <= (std::numeric_limits::min)() + std::numeric_limits::epsilon() + ); + } +} + +#undef CHECK_CLOSE_ABS_DIFF + +#define TEST_TO_FROM_CAST_AROUND_TYPED(VAL,STRING_TYPE) \ + test_value = VAL + std::numeric_limits::epsilon() * i ; \ + converted_val = lexical_cast( lexical_cast(test_value) ); \ + BOOST_CHECK_CLOSE( \ + test_value, \ + converted_val, \ + std::numeric_limits::epsilon()*100 \ + ); + +/* + * For interval [ from_mult*epsilon+VAL, to_mult*epsilon+VAL ], converts float type + * numbers to string[wstring] and then back to float type, then compares initial + * values and generated. + * Step is epsilon + */ +#ifndef BOOST_LCAST_NO_WCHAR_T +# define TEST_TO_FROM_CAST_AROUND(VAL) \ + for(i=from_mult; i<=to_mult; ++i) { \ + TEST_TO_FROM_CAST_AROUND_TYPED(VAL, std::string) \ + TEST_TO_FROM_CAST_AROUND_TYPED(VAL, std::wstring) \ + } +#else +# define TEST_TO_FROM_CAST_AROUND(VAL) \ + for(i=from_mult; i<=to_mult; ++i) { \ + TEST_TO_FROM_CAST_AROUND_TYPED(VAL, std::string) \ + } +#endif + +template +void test_converion_from_to_float_types() +{ + typedef TestType test_t; + test_t test_value; + test_t converted_val; + + int i; + int from_mult = -50; + int to_mult = 50; + + 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 ); + + long double val2; + for(val2 = -1.0e-10L; val2 > -1e11; val2*=10 ) + TEST_TO_FROM_CAST_AROUND( val2 ); + + from_mult = -100; + to_mult = 0; + TEST_TO_FROM_CAST_AROUND( (std::numeric_limits::max)() ); + + from_mult = 0; + to_mult = 100; + TEST_TO_FROM_CAST_AROUND( (std::numeric_limits::min)() ); +} + +#undef TEST_TO_FROM_CAST_AROUND +#undef TEST_TO_FROM_CAST_AROUND_TYPED + + +template +void test_conversion_from_float_to_char(CharT zero) +{ + BOOST_CHECK(lexical_cast(static_cast(0)) == zero + 0); + BOOST_CHECK(lexical_cast(static_cast(1)) == zero + 1); + BOOST_CHECK(lexical_cast(static_cast(2)) == zero + 2); + BOOST_CHECK(lexical_cast(static_cast(3)) == zero + 3); + BOOST_CHECK(lexical_cast(static_cast(4)) == zero + 4); + BOOST_CHECK(lexical_cast(static_cast(5)) == zero + 5); + BOOST_CHECK(lexical_cast(static_cast(6)) == zero + 6); + BOOST_CHECK(lexical_cast(static_cast(7)) == zero + 7); + BOOST_CHECK(lexical_cast(static_cast(8)) == zero + 8); + BOOST_CHECK(lexical_cast(static_cast(9)) == zero + 9); + + BOOST_CHECK_THROW(lexical_cast(static_cast(10)), bad_lexical_cast); + + T t = (std::numeric_limits::max)(); + BOOST_CHECK_THROW(lexical_cast(t), bad_lexical_cast); +} + +template +void test_conversion_from_char_to_float(CharT zero) +{ + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 0)), static_cast(0), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 1)), static_cast(1), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 2)), static_cast(2), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 3)), static_cast(3), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 4)), static_cast(4), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 5)), static_cast(5), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 6)), static_cast(6), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 7)), static_cast(7), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 8)), static_cast(8), (std::numeric_limits::epsilon()*100) ); + BOOST_CHECK_CLOSE(lexical_cast( static_cast(zero + 9)), static_cast(9), (std::numeric_limits::epsilon()*100) ); + + BOOST_CHECK_THROW(lexical_cast( static_cast(zero + 10)), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( static_cast(zero - 1)), bad_lexical_cast); +} + +struct restore_oldloc +{ + std::locale oldloc; + ~restore_oldloc() { std::locale::global(oldloc); } +}; + +template +void test_conversion_from_to_float() +{ char const zero = '0'; + signed char const szero = '0'; + unsigned char const uzero = '0'; + test_conversion_from_float_to_char(zero); + test_conversion_from_char_to_float(zero); + test_conversion_from_float_to_char(szero); + test_conversion_from_char_to_float(szero); + test_conversion_from_float_to_char(uzero); + test_conversion_from_char_to_float(uzero); + #if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) + wchar_t const wzero = L'0'; + test_conversion_from_float_to_char(wzero); + test_conversion_from_char_to_float(wzero); + #endif + + BOOST_CHECK_CLOSE(lexical_cast("+1"), 1, std::numeric_limits::epsilon() ); + BOOST_CHECK_CLOSE(lexical_cast("+9"), 9, std::numeric_limits::epsilon()*9 ); + + BOOST_CHECK_THROW(lexical_cast("++1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("-+9"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("--1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("+-9"), bad_lexical_cast); + + test_converion_to_float_types(); + test_float_typess_for_overflows(); + test_converion_from_to_float_types(); + + + typedef std::numpunct numpunct; + + restore_oldloc guard; + std::locale const& oldloc = guard.oldloc; + + std::string grouping1 = BOOST_USE_FACET(numpunct, oldloc).grouping(); + std::string grouping2(grouping1); + + test_conversion_from_to_float_for_locale(); + + try + { + std::locale newloc(""); + std::locale::global(newloc); + + grouping2 = BOOST_USE_FACET(numpunct, newloc).grouping(); + } + catch(std::exception const& ex) + { + std::string msg("Failed to set system locale: "); + msg += ex.what(); + BOOST_TEST_MESSAGE(msg); + } + + if(grouping1 != grouping2) + test_conversion_from_to_float_for_locale(); + + if(grouping1.empty() && grouping2.empty()) + BOOST_TEST_MESSAGE("Formatting with thousands_sep has not been tested"); +} + + +void test_conversion_from_to_float() +{ + test_conversion_from_to_float(); +} +void test_conversion_from_to_double() +{ + test_conversion_from_to_float(); +} +void test_conversion_from_to_long_double() +{ + test_conversion_from_to_float(); +} + + + + + + + diff --git a/test/lexical_cast_loopback_test.cpp b/test/lexical_cast_loopback_test.cpp index cd058fe..5787996 100644 --- a/test/lexical_cast_loopback_test.cpp +++ b/test/lexical_cast_loopback_test.cpp @@ -64,7 +64,6 @@ void test_round_conversion() } -#if defined(BOOST_MSVC) // See bug http://tinyurl.com/vhpvo template void test_msvc_magic_values() @@ -73,7 +72,6 @@ void test_msvc_magic_values() std::string magic_msvc_s = boost::lexical_cast(magic_msvc); BOOST_CHECK(magic_msvc == lexical_cast(magic_msvc_s)); } -#endif void test_round_conversion_float() { @@ -83,16 +81,12 @@ void test_round_conversion_float() void test_round_conversion_double() { test_round_conversion(); -#if defined(BOOST_MSVC) test_msvc_magic_values(); -#endif } void test_round_conversion_long_double() { test_round_conversion(); -#if defined(BOOST_MSVC) test_msvc_magic_values(); -#endif } diff --git a/test/lexical_cast_wchars_test.cpp b/test/lexical_cast_wchars_test.cpp new file mode 100755 index 0000000..547f226 --- /dev/null +++ b/test/lexical_cast_wchars_test.cpp @@ -0,0 +1,56 @@ +// 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 + +using namespace boost; + +void test_char_types_conversions() +{ +#ifndef BOOST_LCAST_NO_WCHAR_T + const char c_arr[] = "Test array of chars"; + const unsigned char uc_arr[] = "Test array of chars"; + const signed char sc_arr[] = "Test array of chars"; + const wchar_t wc_arr[] =L"Test array of chars"; + + // Following tests depend on realization of std::locale + // and pass for popular compilers and STL realizations + BOOST_CHECK(boost::lexical_cast(c_arr[0]) == wc_arr[0]); + BOOST_CHECK(boost::lexical_cast(c_arr) == std::wstring(wc_arr)); + + BOOST_CHECK(boost::lexical_cast(sc_arr) != std::wstring(wc_arr) ); + BOOST_CHECK(boost::lexical_cast(uc_arr) != std::wstring(wc_arr) ); + + BOOST_CHECK_THROW(boost::lexical_cast(uc_arr[0]), boost::bad_lexical_cast); + BOOST_CHECK_THROW(boost::lexical_cast(sc_arr[0]), boost::bad_lexical_cast); +#endif + BOOST_CHECK(1); +} + +unit_test::test_suite *init_unit_test_suite(int, char *[]) +{ + unit_test_framework::test_suite *suite = + BOOST_TEST_SUITE("lexical_cast char<->wchar_t unit test"); + suite->add(BOOST_TEST_CASE(&test_char_types_conversions)); + + return suite; +}