diff --git a/include/mapnik/util/conversions.hpp b/include/mapnik/util/conversions.hpp index 48f2daa3d..a0c573869 100644 --- a/include/mapnik/util/conversions.hpp +++ b/include/mapnik/util/conversions.hpp @@ -70,12 +70,80 @@ template struct double_policy : boost::spirit::karma::real_policies { typedef boost::spirit::karma::real_policies base_type; - static int floatfield(T n) { return base_type::fmtflags::fixed; } - static unsigned precision(T n) { return static_cast(15 - boost::math::trunc(log10(n))); } + + 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(15 - boost::math::trunc(log10(traits::get_absolute_value(n)))); + } + template static bool dot(OutputIterator& sink, T n, unsigned precision) { - return n ? *sink = '.', true : false; + if (n == 0.0) return true; // avoid trailing zeroes + return base_type::dot(sink, n, precision); } + + template + 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 + using namespace boost; // for remove_const + + 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 remove_const::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 + 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 + + long abs_n = traits::get_absolute_value(n); + bool r = char_inserter::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::call(sink, '0'); + return r && int_inserter<10>::call(sink, abs_n); + } + }; diff --git a/tests/cpp_tests/conversions_test.cpp b/tests/cpp_tests/conversions_test.cpp index a24c807b7..362e09067 100644 --- a/tests/cpp_tests/conversions_test.cpp +++ b/tests/cpp_tests/conversions_test.cpp @@ -49,11 +49,11 @@ int main( int, char*[] ) out.clear(); to_string(out, double(1e-05)); - BOOST_TEST_EQ( out, "0.00001" ); + BOOST_TEST_EQ( out, "1e-05" ); out.clear(); to_string(out, double(-1e-05)); - BOOST_TEST_EQ( out, "-0.00001" ); + BOOST_TEST_EQ( out, "-1e-05" ); out.clear(); to_string(out, double(0.0001));