fix all double to_string conversion failures by using sprintf method in all cases - closes #1741
This commit is contained in:
parent
f7a726445e
commit
00c2ff999d
2 changed files with 21 additions and 109 deletions
|
@ -152,6 +152,24 @@ bool string2float(const char * iter, const char * end, float & result)
|
|||
return r && (iter == end);
|
||||
}
|
||||
|
||||
// double conversion - here we use sprintf over karma to work
|
||||
// around https://github.com/mapnik/mapnik/issues/1741
|
||||
bool to_string(std::string & s, double val)
|
||||
{
|
||||
s.resize(s.capacity());
|
||||
while (true)
|
||||
{
|
||||
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%g", val));
|
||||
if (n2 <= s.size())
|
||||
{
|
||||
s.resize(n2);
|
||||
break;
|
||||
}
|
||||
s.resize(n2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MAPNIK_KARMA_TO_STRING
|
||||
|
||||
bool to_string(std::string & str, int value)
|
||||
|
@ -184,95 +202,6 @@ bool to_string(std::string & str, bool value)
|
|||
return karma::generate(sink, value);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct double_policy : boost::spirit::karma::real_policies<T>
|
||||
{
|
||||
typedef boost::spirit::karma::real_policies<T> base_type;
|
||||
|
||||
static int floatfield(T n) {
|
||||
using namespace boost::spirit; // for traits
|
||||
|
||||
if (traits::test_zero(n))
|
||||
return base_type::fmtflags::fixed;
|
||||
|
||||
T abs_n = traits::get_absolute_value(n);
|
||||
return (abs_n >= 1e16 || abs_n < 1e-4)
|
||||
? base_type::fmtflags::scientific : base_type::fmtflags::fixed;
|
||||
}
|
||||
|
||||
static unsigned precision(T n) {
|
||||
if ( n == 0.0 ) return 0;
|
||||
using namespace boost::spirit; // for traits
|
||||
return static_cast<unsigned>(15 - boost::math::trunc(log10(traits::get_absolute_value(n))));
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
static bool dot(OutputIterator& sink, T n, unsigned precision) {
|
||||
if (n == 0.0) return true; // avoid trailing zeroes
|
||||
return base_type::dot(sink, n, precision);
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
static bool fraction_part (OutputIterator& sink, T n
|
||||
, unsigned precision_, unsigned precision)
|
||||
{
|
||||
// NOTE: copied from karma only to avoid trailing zeroes
|
||||
// (maybe a bug ?)
|
||||
|
||||
// allow for ADL to find the correct overload for floor and log10
|
||||
using namespace std;
|
||||
|
||||
using namespace boost::spirit; // for traits
|
||||
using namespace boost::spirit::karma; // for char_inserter
|
||||
|
||||
if ( traits::test_zero(n) ) return true; // this part added to karma
|
||||
|
||||
// The following is equivalent to:
|
||||
// generate(sink, right_align(precision, '0')[ulong], n);
|
||||
// but it's spelled out to avoid inter-modular dependencies.
|
||||
|
||||
typename boost::remove_const<T>::type digits =
|
||||
(traits::test_zero(n) ? 0 : floor(log10(n))) + 1;
|
||||
bool r = true;
|
||||
for (/**/; r && digits < precision_; digits = digits + 1)
|
||||
r = char_inserter<>::call(sink, '0');
|
||||
if (precision && r)
|
||||
r = int_inserter<10>::call(sink, n);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename CharEncoding, typename Tag, typename OutputIterator>
|
||||
static bool exponent (OutputIterator& sink, long n)
|
||||
{
|
||||
// NOTE: copied from karma to force sign in exponent
|
||||
const bool force_sign = true;
|
||||
|
||||
using namespace boost::spirit; // for traits
|
||||
using namespace boost::spirit::karma; // for char_inserter, sign_inserter
|
||||
|
||||
unsigned long abs_n = traits::get_absolute_value(n);
|
||||
bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
|
||||
sign_inserter::call(sink, traits::test_zero(n)
|
||||
, traits::test_negative(n), force_sign);
|
||||
|
||||
// the C99 Standard requires at least two digits in the exponent
|
||||
if (r && abs_n < 10)
|
||||
r = char_inserter<CharEncoding, Tag>::call(sink, '0');
|
||||
return r && int_inserter<10>::call(sink, abs_n);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
bool to_string(std::string & str, double value)
|
||||
{
|
||||
namespace karma = boost::spirit::karma;
|
||||
typedef karma::real_generator<double, detail::double_policy<double> > double_type;
|
||||
std::back_insert_iterator<std::string> sink(str);
|
||||
return karma::generate(sink, double_type(), value);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool to_string(std::string & s, int val)
|
||||
|
@ -332,22 +261,6 @@ bool to_string(std::string & s, bool val)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool to_string(std::string & s, double val)
|
||||
{
|
||||
s.resize(s.capacity());
|
||||
while (true)
|
||||
{
|
||||
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%g", val));
|
||||
if (n2 <= s.size())
|
||||
{
|
||||
s.resize(n2);
|
||||
break;
|
||||
}
|
||||
s.resize(n2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // end namespace util
|
||||
|
|
|
@ -93,18 +93,18 @@ int main( int, char*[] )
|
|||
BOOST_TEST_EQ( out, "1e-10" );
|
||||
out.clear();
|
||||
|
||||
// critical failure when karam is used
|
||||
|
||||
to_string(out, double(-1.234e+16));
|
||||
BOOST_TEST_EQ( out, "-1.234e+16" );
|
||||
out.clear();
|
||||
|
||||
// critical failure when karam is used
|
||||
// https://github.com/mapnik/mapnik/issues/1741
|
||||
// https://github.com/mapbox/tilemill/issues/1456
|
||||
to_string(out, double(8.3));
|
||||
BOOST_TEST_EQ( out, "8.3" );
|
||||
out.clear();
|
||||
|
||||
// non-critical failures if karma is used
|
||||
/*
|
||||
to_string(out, double(0.0001234567890123456));
|
||||
// TODO: https://github.com/mapnik/mapnik/issues/1676
|
||||
BOOST_TEST_EQ( out, "0.000123457" );
|
||||
|
@ -189,7 +189,6 @@ int main( int, char*[] )
|
|||
to_string(out, double(1234000000000000));
|
||||
BOOST_TEST_EQ( out, "1.234e+15" );
|
||||
out.clear();
|
||||
*/
|
||||
|
||||
to_string(out, double(1e+16));
|
||||
BOOST_TEST_EQ( out, "1e+16" );
|
||||
|
|
Loading…
Reference in a new issue