From b5552e351cfc087ea3b6350101b7824b1262e2ea Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 29 Aug 2013 11:41:28 +0000 Subject: [PATCH] lexical_cast.hpp improvements: better overflow detections for float conversions, more `const` modifiers (refs #9046) [SVN r85512] --- include/boost/lexical_cast.hpp | 139 ++++++++++++++------------------- 1 file changed, 57 insertions(+), 82 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 6b83f63..2b84fb7 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -891,14 +891,11 @@ namespace boost { const CharT plus = lcast_char_constants::plus; const int inifinity_size = 8; - bool has_minus = false; /* Parsing +/- */ - if( *begin == minus) - { + bool const has_minus = (*begin == minus); + if (has_minus || *begin == plus) { ++ begin; - has_minus = true; } - else if( *begin == plus ) ++begin; if( end-begin < 3 ) return false; if( lc_iequal(begin, lc_nan, lc_NAN, 3) ) @@ -1075,6 +1072,25 @@ namespace boost { template inline bool lcast_ret_float(T& value, const CharT* begin, const CharT* end) { + value = static_cast(0); + if (begin == end) return false; + if (parse_inf_nan(begin, end, value)) return true; + + 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; + + typedef BOOST_DEDUCED_TYPENAME Traits::int_type int_type; + int_type const zero = Traits::to_int_type(czero); + + /* Getting the plus/minus sign */ + bool const has_minus = Traits::eq(*begin, minus); + if (has_minus || Traits::eq(*begin, plus)) { + ++ begin; + if (begin == end) return false; + } #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE std::locale loc; @@ -1094,43 +1110,17 @@ namespace boost { 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 = static_cast(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; - typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type::wide_result_t wide_result_t; - int_type const zero = Traits::to_int_type(czero); - if (begin == end) return false; - - /* Getting the plus/minus sign */ - bool has_minus = false; - if (Traits::eq(*begin, minus) ) { - ++ begin; - has_minus = true; - if (begin == end) return false; - } else if (Traits::eq(*begin, plus) ) { - ++begin; - if (begin == end) return false; - } - bool found_decimal = false; bool found_number_before_exp = false; - int pow_of_10 = 0; + typedef int pow_of_10_t; + pow_of_10_t pow_of_10 = 0; + + typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type::type mantissa_type; mantissa_type mantissa=0; bool is_mantissa_full = false; - char length_since_last_delim = 0; - while ( begin != end ) - { + while (begin != end) { if (found_decimal) { /* We allow no thousand_separators after decimal point */ @@ -1157,15 +1147,14 @@ namespace boost { * occur, them we only increase multiplyer */ const mantissa_type tmp_sub_value = static_cast(*begin - zero); - if( !(is_mantissa_full - || ((std::numeric_limits::max)() - tmp_sub_value) / 10u < mantissa) + if( is_mantissa_full + || ((std::numeric_limits::max)() - tmp_sub_value) / 10u < mantissa ) - { - mantissa = static_cast(mantissa * 10 + tmp_sub_value); - } else { is_mantissa_full = true; ++ pow_of_10; + } else { + mantissa = static_cast(mantissa * 10 + tmp_sub_value); } found_number_before_exp = true; @@ -1188,12 +1177,12 @@ namespace boost { ) return false; #endif - if(Traits::eq(*begin, decimal_point)) { + if (Traits::eq(*begin, decimal_point)) { ++ begin; found_decimal = true; if (!found_number_before_exp && begin==end) return false; continue; - }else { + } else { if (!found_number_before_exp) return false; break; } @@ -1245,48 +1234,44 @@ namespace boost { ++ begin; if ( begin == end ) return false; - bool exp_has_minus = false; - if(Traits::eq(*begin, minus)) { - exp_has_minus = true; + bool const exp_has_minus = Traits::eq(*begin, minus); + if (exp_has_minus || Traits::eq(*begin, plus)) { ++ begin; - if ( begin == end ) return false; - } else if (Traits::eq(*begin, plus)) { - ++ begin; - if ( begin == end ) return false; + 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*/ + pow_of_10_t exp_pow_of_10 = 0; + while (begin != end) { + pow_of_10_t const sub_value = *begin - zero; + + if ( *begin < czero || *begin >= czero + 10 + || ((std::numeric_limits::max)() - sub_value) / 10 < exp_pow_of_10) return false; exp_pow_of_10 *= 10; - exp_pow_of_10 += *begin - zero; + exp_pow_of_10 += sub_value; ++ 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; - } + if (exp_has_minus) { + if ((std::numeric_limits::min)() + exp_pow_of_10 > pow_of_10) + return false; // failed overflow check + pow_of_10 -= exp_pow_of_10; + } else { + if ((std::numeric_limits::max)() - exp_pow_of_10 < pow_of_10) + return false; // failed overflow check + 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)). */ + typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type::wide_result_t wide_result_t; const wide_result_t result = std::pow(static_cast(10.0), pow_of_10) * mantissa; value = static_cast( has_minus ? (boost::math::changesign)(result) : result); - if ( (boost::math::isinf)(value) || (boost::math::isnan)(value) ) return false; - - return true; + return !((boost::math::isinf)(value) || (boost::math::isnan)(value)); } // Unsilence buggy MS warnings like C4244: '+=' : conversion from 'int' to 'unsigned short', possible loss of data #if defined(_MSC_VER) && (_MSC_VER == 1400) @@ -1805,15 +1790,10 @@ namespace boost { if (start == finish) return false; CharT const minus = lcast_char_constants::minus; CharT const plus = lcast_char_constants::plus; - bool has_minus = false; + bool const has_minus = Traits::eq(minus, *start); /* We won`t use `start' any more, so no need in decrementing it after */ - if ( Traits::eq(minus,*start) ) - { - ++start; - has_minus = true; - } else if ( Traits::eq( plus, *start ) ) - { + if (has_minus || Traits::eq(plus, *start)) { ++start; } @@ -1833,16 +1813,11 @@ namespace boost { CharT const minus = lcast_char_constants::minus; CharT const plus = lcast_char_constants::plus; typedef BOOST_DEDUCED_TYPENAME make_unsigned::type utype; - utype out_tmp =0; - bool has_minus = false; + utype out_tmp = 0; + bool const has_minus = Traits::eq(minus, *start); /* We won`t use `start' any more, so no need in decrementing it after */ - if ( Traits::eq(minus,*start) ) - { - ++start; - has_minus = true; - } else if ( Traits::eq(plus, *start) ) - { + if (has_minus || Traits::eq(plus, *start)) { ++start; }