Merge pull request #1697 from mapnik/faster_to_string

default to fast, simple, and correct (per ostringstream) to_string conversions
This commit is contained in:
Artem Pavlenko 2013-01-21 10:01:09 -08:00
commit 27554cbef8
2 changed files with 233 additions and 55 deletions

View file

@ -24,7 +24,8 @@
#include <mapnik/util/conversions.hpp>
#include <mapnik/value_types.hpp>
// boost
#include <cstring>
#include <boost/spirit/include/qi.hpp>
#define BOOST_SPIRIT_AUTO(domain_, name, expr) \
@ -34,20 +35,18 @@
boost::spirit::domain_::domain, name##_expr_type); \
BOOST_AUTO(name, boost::proto::deep_copy(expr)); \
#include <cmath> // log10
//#define MAPNIK_KARMA_TO_STRING
// boost
#ifdef MAPNIK_KARMA_TO_STRING
#include <boost/version.hpp>
#include <boost/math/special_functions/trunc.hpp> // trunc to avoid needing C++11
#if BOOST_VERSION >= 104500
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/karma.hpp>
#else
#include <boost/lexical_cast.hpp>
#if BOOST_VERSION < 104500
#error you must have >= boost 104500 to use karma for string output
#endif
#include <cstring>
#include <boost/spirit/include/karma.hpp>
#include <cmath> // log10
#include <boost/math/special_functions/trunc.hpp> // trunc to avoid needing C++11
#endif
namespace mapnik {
@ -62,8 +61,6 @@ BOOST_SPIRIT_AUTO(qi, LONGLONG, qi::long_long)
BOOST_SPIRIT_AUTO(qi, FLOAT, qi::float_)
BOOST_SPIRIT_AUTO(qi, DOUBLE, qi::double_)
struct bool_symbols : qi::symbols<char,bool>
{
bool_symbols()
@ -163,7 +160,7 @@ bool string2float(const char * value, float & result)
return r && (iter == end);
}
#if BOOST_VERSION >= 104500
#ifdef MAPNIK_KARMA_TO_STRING
bool to_string(std::string & str, int value)
{
@ -286,48 +283,89 @@ bool to_string(std::string & str, double value)
#else
template <typename T>
bool to_string_lexical(std::string & str, T value)
bool to_string(std::string & s, int val)
{
try
s.resize(s.capacity());
while (true)
{
str = boost::lexical_cast<T>(value);
return true;
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%d", val));
if (n2 <= s.size())
{
s.resize(n2);
break;
}
s.resize(n2);
}
catch (std::exception const& ex)
{
return false;
}
}
bool to_string(std::string & str, int value)
{
return to_string_lexical(str, value);
return true;
}
#ifdef BIGINT
bool to_string(std::string & str, mapnik::value_integer value)
bool to_string(std::string & s, mapnik::value_integer val)
{
return to_string_lexical(str, value);
s.resize(s.capacity());
while (true)
{
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%lld", val));
if (n2 <= s.size())
{
s.resize(n2);
break;
}
s.resize(n2);
}
return true;
}
bool to_string(std::string & str, unsigned value)
{
return to_string_lexical(str, value);
}
bool to_string(std::string & str, bool value)
{
return to_string_lexical(str, value);
}
bool to_string(std::string & str, double value)
{
return to_string_lexical(str, value);
}
#endif
bool to_string(std::string & s, unsigned val)
{
s.resize(s.capacity());
while (true)
{
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%u", val));
if (n2 <= s.size())
{
s.resize(n2);
break;
}
s.resize(n2);
}
return true;
}
bool to_string(std::string & s, bool val)
{
if (val) s = "true";
else s = "false";
return true;
}
bool to_string(std::string & s, double val)
{
double abs_n = std::fabs(val);
std::string format;
if (abs_n >= 1e16 || abs_n < 1e-4)
{
format = "%e";
}
else
{
format = "%g";
}
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

View file

@ -1,4 +1,5 @@
#include <boost/version.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/util/conversions.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <iostream>
@ -69,16 +70,104 @@ int main( int, char*[] )
BOOST_TEST_EQ( out, "0.000123457" );
out.clear();
to_string(out, double(1000000000000000));
BOOST_TEST_EQ( out, "1000000000000000" );
to_string(out, double(0.0001));
BOOST_TEST_EQ( out, "0.0001" );
out.clear();
to_string(out, double(0.00001));
BOOST_TEST_EQ( out, "1e-05" );
out.clear();
to_string(out, double(0.000001));
BOOST_TEST_EQ( out, "1e-06" );
out.clear();
to_string(out, double(0.0000001));
BOOST_TEST_EQ( out, "1e-07" );
out.clear();
to_string(out, double(0.00000001));
BOOST_TEST_EQ( out, "1e-08" );
out.clear();
to_string(out, double(0.000000001));
BOOST_TEST_EQ( out, "1e-09" );
out.clear();
to_string(out, double(0.0000000001));
BOOST_TEST_EQ( out, "1e-10" );
out.clear();
to_string(out, double(0.00000000001));
BOOST_TEST_EQ( out, "1e-11" );
out.clear();
to_string(out, double(0.000000000001));
BOOST_TEST_EQ( out, "1e-12" );
out.clear();
to_string(out, double(0.0000000000001));
BOOST_TEST_EQ( out, "1e-13" );
out.clear();
to_string(out, double(0.00000000000001));
BOOST_TEST_EQ( out, "1e-14" );
out.clear();
to_string(out, double(0.000000000000001));
BOOST_TEST_EQ( out, "1e-15" );
out.clear();
to_string(out, double(100000));
BOOST_TEST_EQ( out, "100000" );
out.clear();
to_string(out, double(1000000));
BOOST_TEST_EQ( out, "1e+06" );
out.clear();
to_string(out, double(10000000));
BOOST_TEST_EQ( out, "1e+07" );
out.clear();
to_string(out, double(100000000));
BOOST_TEST_EQ( out, "1e+08" );
out.clear();
to_string(out, double(1000000000));
BOOST_TEST_EQ( out, "1e+09" );
out.clear();
to_string(out, double(10000000000));
BOOST_TEST_EQ( out, "1e+10" );
out.clear();
to_string(out, double(100000000000));
BOOST_TEST_EQ( out, "1e+11" );
out.clear();
to_string(out, double(1000000000000));
BOOST_TEST_EQ( out, "1e+12" );
out.clear();
to_string(out, double(10000000000000));
BOOST_TEST_EQ( out, "1e+13" );
out.clear();
to_string(out, double(100000000000000));
BOOST_TEST_EQ( out, "1e+14" );
out.clear();
to_string(out, double(1000000000000005));
BOOST_TEST_EQ( out, "1e+15" );
out.clear();
to_string(out, double(-1000000000000000));
BOOST_TEST_EQ( out, "-1000000000000000" );
BOOST_TEST_EQ( out, "-1e+15" );
out.clear();
to_string(out, double(100000000000000.1));
BOOST_TEST_EQ( out, "100000000000000.1" );
BOOST_TEST_EQ( out, "1e+14" );
out.clear();
to_string(out, double(1.00001));
@ -86,7 +175,11 @@ int main( int, char*[] )
out.clear();
to_string(out, double(1234000000000000));
BOOST_TEST_EQ( out, "1234000000000000" );
BOOST_TEST_EQ( out, "1.234e+15" );
out.clear();
to_string(out, double(1e+16));
BOOST_TEST_EQ( out, "1e+16" );
out.clear();
to_string(out, double(1.234e+16));
@ -96,12 +189,59 @@ int main( int, char*[] )
to_string(out, double(-1.234e+16));
BOOST_TEST_EQ( out, "-1.234e+16" );
out.clear();
// https://github.com/mapbox/tilemill/issues/1456
to_string(out, double(8.3));
BOOST_TEST_EQ( out, "8.3" );
out.clear();
// Test int
to_string(out, int(2));
// int
to_string(out, int(2));
BOOST_TEST_EQ( out, "2" );
out.clear();
to_string(out, int(0));
BOOST_TEST_EQ( out, "0" );
out.clear();
to_string(out, int(-2));
BOOST_TEST_EQ( out, "-2" );
out.clear();
to_string(out, int(2147483647));
BOOST_TEST_EQ( out, "2147483647" );
out.clear();
to_string(out, int(-2147483648));
BOOST_TEST_EQ( out, "-2147483648" );
out.clear();
// unsigned
to_string(out, unsigned(4294967295));
BOOST_TEST_EQ( out, "4294967295" );
out.clear();
#ifdef BIGINT
// long long
to_string(out,mapnik::value_integer(-0));
BOOST_TEST_EQ( out, "0" );
out.clear();
to_string(out,mapnik::value_integer(-2));
BOOST_TEST_EQ( out, "-2" );
out.clear();
to_string(out,mapnik::value_integer(9223372036854775807));
BOOST_TEST_EQ( out, "9223372036854775807" );
out.clear();
#endif
// bool
to_string(out, true);
BOOST_TEST_EQ( out, "true" );
out.clear();
to_string(out, false);
BOOST_TEST_EQ( out, "false" );
out.clear();
}
catch (std::exception const & ex)
{