Permalink
Browse files

Implement scientific notation for double-to-string

Also fixes tests for 1e5 expecting fixed precision rather than
scientific notation (stringstream gives scientific notation indeed)

The only still failing test now is the one having less than 16
significant digits of precision, due to the boost bug:
https://svn.boost.org/trac/boost/ticket/7785
  • Loading branch information...
1 parent 4040eeb commit e8b7b82bbb03ca079a2725dcd71ff0c257acf7be Sandro Santilli committed Dec 17, 2012
Showing with 73 additions and 5 deletions.
  1. +71 −3 include/mapnik/util/conversions.hpp
  2. +2 −2 tests/cpp_tests/conversions_test.cpp
@@ -70,12 +70,80 @@ 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) { return base_type::fmtflags::fixed; }
- static unsigned precision(T n) { return static_cast<unsigned>(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<unsigned>(15 - boost::math::trunc(log10(traits::get_absolute_value(n))));
+ }
+
template <typename OutputIterator>
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 <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
+ 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<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
+
+ 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);
+ }
+
};
@@ -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));

0 comments on commit e8b7b82

Please sign in to comment.