blob: 9ef71268658026c0031bc4f7065add5b960237e3 [file] [log] [blame]
// Copyright Kevlin Henney, 2000-2005.
// Copyright Alexander Nasonov, 2006-2010.
// Copyright Antony Polukhin, 2011-2014.
//
// 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)
//
// what: lexical_cast custom keyword cast
// who: contributed by Kevlin Henney,
// enhanced with contributions from Terje Slettebo,
// with additional fixes and suggestions from Gennaro Prota,
// Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov,
// Alexander Nasonov, Antony Polukhin, Justin Viiret, Michael Hofmann,
// Cheng Yang, Matthew Bradbury, David W. Birdsall, Pavel Korzh and other Boosters
// when: November 2000, March 2003, June 2005, June 2006, March 2011 - 2014
#ifndef BOOST_LEXICAL_CAST_DETAIL_LCAST_FLOAT_CONVERTERS_HPP
#define BOOST_LEXICAL_CAST_DETAIL_LCAST_FLOAT_CONVERTERS_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <cstddef>
#include <cmath>
#include <boost/limits.hpp>
#include <boost/detail/workaround.hpp>
#ifndef BOOST_NO_STD_LOCALE
# include <locale>
#else
# ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
// Getting error at this point means, that your STL library is old/lame/misconfigured.
// If nothing can be done with STL library, define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE,
// but beware: lexical_cast will understand only 'C' locale delimeters and thousands
// separators.
# error "Unable to use <locale> header. Define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE to force "
# error "boost::lexical_cast to use only 'C' locale during conversions."
# endif
#endif
#include <boost/lexical_cast/detail/lcast_char_constants.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
namespace boost {
namespace detail // lcast_ret_float
{
// Silence buggy MS warnings like C4244: '+=' : conversion from 'int' to 'unsigned short', possible loss of data
#if defined(_MSC_VER) && (_MSC_VER == 1400)
# pragma warning(push)
# pragma warning(disable:4244)
#endif
template <class T>
struct mantissa_holder_type
{
/* Can not be used with this type */
};
template <>
struct mantissa_holder_type<float>
{
typedef unsigned int type;
typedef double wide_result_t;
};
template <>
struct mantissa_holder_type<double>
{
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
typedef long double wide_result_t;
#if defined(BOOST_HAS_LONG_LONG)
typedef boost::ulong_long_type type;
#elif defined(BOOST_HAS_MS_INT64)
typedef unsigned __int64 type;
#endif
#endif
};
template<class Traits, class T, class CharT>
inline bool lcast_ret_float(T& value, const CharT* begin, const CharT* const end)
{
value = static_cast<T>(0);
if (begin == end) return false;
if (parse_inf_nan(begin, end, value)) return true;
CharT const czero = lcast_char_constants<CharT>::zero;
CharT const minus = lcast_char_constants<CharT>::minus;
CharT const plus = lcast_char_constants<CharT>::plus;
CharT const capital_e = lcast_char_constants<CharT>::capital_e;
CharT const lowercase_e = lcast_char_constants<CharT>::lowercase_e;
/* 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;
typedef std::numpunct<CharT> numpunct;
numpunct const& np = BOOST_USE_FACET(numpunct, loc);
std::string const grouping(
(loc == std::locale::classic())
? std::string()
: np.grouping()
);
std::string::size_type const grouping_size = grouping.size();
CharT const thousands_sep = static_cast<CharT>(grouping_size ? np.thousands_sep() : 0);
CharT const decimal_point = np.decimal_point();
bool found_grouping = false;
std::string::size_type last_grouping_pos = grouping_size - 1;
#else
CharT const decimal_point = lcast_char_constants<CharT>::c_decimal_separator;
#endif
bool found_decimal = false;
bool found_number_before_exp = false;
typedef int pow_of_10_t;
pow_of_10_t pow_of_10 = 0;
typedef BOOST_DEDUCED_TYPENAME mantissa_holder_type<T>::type mantissa_type;
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 */
const mantissa_type tmp_sub_value = static_cast<mantissa_type>(*begin - czero);
if (Traits::eq(*begin, lowercase_e) || Traits::eq(*begin, capital_e)) break;
if ( *begin < czero || *begin >= czero + 10 ) return false;
if ( is_mantissa_full
|| ((std::numeric_limits<mantissa_type>::max)() - tmp_sub_value) / 10u < mantissa
) {
is_mantissa_full = true;
++ begin;
continue;
}
-- pow_of_10;
mantissa = static_cast<mantissa_type>(mantissa * 10 + tmp_sub_value);
found_number_before_exp = true;
} else {
if (*begin >= czero && *begin < czero + 10) {
/* Checking for mantissa overflow. If overflow will
* occur, them we only increase multiplyer
*/
const mantissa_type tmp_sub_value = static_cast<mantissa_type>(*begin - czero);
if( is_mantissa_full
|| ((std::numeric_limits<mantissa_type>::max)() - tmp_sub_value) / 10u < mantissa
)
{
is_mantissa_full = true;
++ pow_of_10;
} else {
mantissa = static_cast<mantissa_type>(mantissa * 10 + tmp_sub_value);
}
found_number_before_exp = true;
++ length_since_last_delim;
} else if (Traits::eq(*begin, decimal_point) || Traits::eq(*begin, lowercase_e) || Traits::eq(*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 (Traits::eq(*begin, decimal_point)) {
++ begin;
found_decimal = true;
if (!found_number_before_exp && begin==end) return false;
continue;
} else {
if (!found_number_before_exp) return false;
break;
}
}
#ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
else if (grouping_size && Traits::eq(*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_size) return false;
} else {
/* Delimiter at the begining ',000' */
if (!length_since_last_delim) return false;
found_grouping = true;
if (length_since_last_delim > 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 && (Traits::eq(*begin, lowercase_e) || Traits::eq(*begin, capital_e))) {
++ begin;
if (begin == end) return false;
bool const exp_has_minus = Traits::eq(*begin, minus);
if (exp_has_minus || Traits::eq(*begin, plus)) {
++ begin;
if (begin == end) return false;
}
pow_of_10_t exp_pow_of_10 = 0;
while (begin != end) {
pow_of_10_t const sub_value = *begin - czero;
if ( *begin < czero || *begin >= czero + 10
|| ((std::numeric_limits<pow_of_10_t>::max)() - sub_value) / 10 < exp_pow_of_10)
return false;
exp_pow_of_10 *= 10;
exp_pow_of_10 += sub_value;
++ begin;
};
if (exp_has_minus) {
if ((std::numeric_limits<pow_of_10_t>::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<pow_of_10_t>::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<T>::wide_result_t wide_result_t;
const wide_result_t result = std::pow(static_cast<wide_result_t>(10.0), pow_of_10) * mantissa;
value = static_cast<T>( has_minus ? (boost::math::changesign)(result) : result);
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)
# pragma warning(pop)
#endif
}
} // namespace boost
#endif // BOOST_LEXICAL_CAST_DETAIL_LCAST_FLOAT_CONVERTERS_HPP