// Copyright (c) 2001-2011 Hartmut Kaiser | |
// | |
// 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) | |
#if !defined(BOOST_SPIRIT_KARMA_REAL_UTILS_FEB_23_2007_0841PM) | |
#define BOOST_SPIRIT_KARMA_REAL_UTILS_FEB_23_2007_0841PM | |
#if defined(_MSC_VER) | |
#pragma once | |
#endif | |
#include <boost/config.hpp> | |
#include <boost/config/no_tr1/cmath.hpp> | |
#include <boost/detail/workaround.hpp> | |
#include <limits> | |
#include <boost/spirit/home/support/char_class.hpp> | |
#include <boost/spirit/home/support/unused.hpp> | |
#include <boost/spirit/home/support/detail/pow10.hpp> | |
#include <boost/spirit/home/support/detail/sign.hpp> | |
#include <boost/spirit/home/karma/detail/generate_to.hpp> | |
#include <boost/spirit/home/karma/detail/string_generate.hpp> | |
#include <boost/spirit/home/karma/numeric/detail/numeric_utils.hpp> | |
namespace boost { namespace spirit { namespace karma | |
{ | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// The real_inserter template takes care of the floating point number to | |
// string conversion. The Policies template parameter is used to allow | |
// customization of the formatting process | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename T> | |
struct real_policies; | |
template <typename T | |
, typename Policies = real_policies<T> | |
, typename CharEncoding = unused_type | |
, typename Tag = unused_type> | |
struct real_inserter | |
{ | |
template <typename OutputIterator> | |
static bool | |
call (OutputIterator& sink, float n, Policies const& p = Policies()) | |
{ | |
int fpclass = (math::fpclassify)(n); | |
if ((int)FP_NAN == fpclass) { | |
return Policies::template nan<CharEncoding, Tag>( | |
sink, n, p.force_sign(n)); | |
} | |
else if ((int)FP_INFINITE == fpclass) { | |
return Policies::template inf<CharEncoding, Tag>( | |
sink, n, p.force_sign(n)); | |
} | |
return p.template call<real_inserter>(sink, n, p); | |
} | |
template <typename OutputIterator> | |
static bool | |
call (OutputIterator& sink, double n, Policies const& p = Policies()) | |
{ | |
int fpclass = (math::fpclassify)(n); | |
if ((int)FP_NAN == fpclass) { | |
return Policies::template nan<CharEncoding, Tag>( | |
sink, n, p.force_sign(n)); | |
} | |
else if ((int)FP_INFINITE == fpclass) { | |
return Policies::template inf<CharEncoding, Tag>( | |
sink, n, p.force_sign(n)); | |
} | |
return p.template call<real_inserter>(sink, n, p); | |
} | |
template <typename OutputIterator> | |
static bool | |
call (OutputIterator& sink, long double n, Policies const& p = Policies()) | |
{ | |
int fpclass = (math::fpclassify)(n); | |
if ((int)FP_NAN == fpclass) { | |
return Policies::template nan<CharEncoding, Tag>( | |
sink, n, p.force_sign(n)); | |
} | |
else if ((int)FP_INFINITE == fpclass) { | |
return Policies::template inf<CharEncoding, Tag>( | |
sink, n, p.force_sign(n)); | |
} | |
return p.template call<real_inserter>(sink, n, p); | |
} | |
template <typename OutputIterator, typename U> | |
static bool | |
call (OutputIterator& sink, U n, Policies const& p = Policies()) | |
{ | |
// we have no means of testing whether the number is normalized if | |
// the type is not float, double or long double | |
return p.template call<real_inserter>(sink, T(n), p); | |
} | |
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) | |
# pragma warning(push) | |
# pragma warning(disable: 4100) // 'p': unreferenced formal parameter | |
# pragma warning(disable: 4127) // conditional expression is constant | |
# pragma warning(disable: 4267) // conversion from 'size_t' to 'unsigned int', possible loss of data | |
#endif | |
/////////////////////////////////////////////////////////////////////// | |
// This is the workhorse behind the real generator | |
/////////////////////////////////////////////////////////////////////// | |
template <typename OutputIterator, typename U> | |
static bool | |
call_n (OutputIterator& sink, U n, Policies const& p) | |
{ | |
// prepare sign and get output format | |
bool force_sign = p.force_sign(n); | |
bool sign_val = false; | |
int flags = p.floatfield(n); | |
if (detail::is_negative(n)) | |
{ | |
n = -n; | |
sign_val = true; | |
} | |
// The scientific representation requires the normalization of the | |
// value to convert. | |
// get correct precision for generated number | |
unsigned precision = p.precision(n); | |
if (std::numeric_limits<U>::digits10) | |
{ | |
// limit generated precision to digits10, if defined | |
precision = (std::min)(precision, | |
(unsigned)std::numeric_limits<U>::digits10 + 1); | |
} | |
// allow for ADL to find the correct overloads for log10 et.al. | |
using namespace std; | |
U dim = 0; | |
if (0 == (Policies::fmtflags::fixed & flags) && !detail::is_zero(n)) | |
{ | |
dim = log10(n); | |
if (dim > 0) | |
n /= spirit::detail::pow10<U>(detail::truncate_to_long::call(dim)); | |
else if (n < 1.) { | |
long exp = detail::truncate_to_long::call(-dim); | |
if (exp != -dim) | |
++exp; | |
dim = -exp; | |
n *= spirit::detail::pow10<U>(exp); | |
} | |
} | |
// prepare numbers (sign, integer and fraction part) | |
U integer_part; | |
U precexp = spirit::detail::pow10<U>(precision); | |
U fractional_part = modf(n, &integer_part); | |
fractional_part = floor(fractional_part * precexp + U(0.5)); | |
if (fractional_part >= precexp) | |
{ | |
fractional_part = floor(fractional_part - precexp); | |
integer_part += 1; // handle rounding overflow | |
} | |
// if trailing zeros are to be omitted, normalize the precision and | |
// fractional part | |
U long_int_part = floor(integer_part); | |
U long_frac_part = fractional_part; | |
unsigned prec = precision; | |
if (!p.trailing_zeros(n)) | |
{ | |
U frac_part_floor = long_frac_part; | |
if (0 != long_frac_part) { | |
// remove the trailing zeros | |
while (0 != prec && | |
0 == detail::remainder<10>::call(long_frac_part)) | |
{ | |
long_frac_part = detail::divide<10>::call(long_frac_part); | |
--prec; | |
} | |
} | |
else { | |
// if the fractional part is zero, we don't need to output | |
// any additional digits | |
prec = 0; | |
} | |
if (precision != prec) | |
{ | |
long_frac_part = frac_part_floor / | |
spirit::detail::pow10<U>(precision-prec); | |
} | |
} | |
// call the actual generating functions to output the different parts | |
if (sign_val && detail::is_zero(long_int_part) && | |
detail::is_zero(long_frac_part)) | |
{ | |
sign_val = false; // result is zero, no sign please | |
} | |
// generate integer part | |
bool r = p.integer_part(sink, long_int_part, sign_val, force_sign); | |
// generate decimal point | |
r = r && p.dot(sink, long_frac_part, precision); | |
// generate fractional part with the desired precision | |
r = r && p.fraction_part(sink, long_frac_part, prec, precision); | |
if (r && 0 == (Policies::fmtflags::fixed & flags)) { | |
return p.template exponent<CharEncoding, Tag>(sink, | |
detail::truncate_to_long::call(dim)); | |
} | |
return r; | |
} | |
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) | |
# pragma warning(pop) | |
#endif | |
}; | |
}}} | |
#endif | |