summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/util/string.hpp14
-rw-r--r--src/mbgl/util/dtoa.cpp97
-rw-r--r--src/mbgl/util/dtoa.hpp22
-rw-r--r--test/test.gypi1
-rw-r--r--test/util/number_conversions.cpp55
5 files changed, 189 insertions, 0 deletions
diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp
index d422a9d8f7..5d3631e190 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -5,6 +5,8 @@
#include <cassert>
#include <exception>
+#include <mbgl/util/dtoa.hpp>
+
namespace mbgl {
namespace util {
@@ -21,6 +23,18 @@ inline std::string toString(uint8_t num) {
return std::to_string(unsigned(num));
}
+inline std::string toString(float num) {
+ return dtoa(num);
+}
+
+inline std::string toString(double num) {
+ return dtoa(num);
+}
+
+inline std::string toString(long double num) {
+ return dtoa(num);
+}
+
inline std::string toString(std::exception_ptr error) {
assert(error);
diff --git a/src/mbgl/util/dtoa.cpp b/src/mbgl/util/dtoa.cpp
new file mode 100644
index 0000000000..7ccee345f6
--- /dev/null
+++ b/src/mbgl/util/dtoa.cpp
@@ -0,0 +1,97 @@
+#include "dtoa.hpp"
+
+#include <rapidjson/internal/dtoa.h>
+
+namespace mbgl {
+namespace util {
+
+namespace {
+
+// From https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h
+
+inline char* Prettify(char* buffer, int length, int k) {
+ constexpr int maxDecimalPlaces = 324;
+ const int kk = length + k; // 10^(kk-1) <= v < 10^kk
+
+ if (0 <= k && kk <= 21) {
+ // 1234e7 -> 12340000000
+ for (int i = length; i < kk; i++)
+ buffer[i] = '0';
+ return &buffer[kk];
+ }
+ else if (0 < kk && kk <= 21) {
+ // 1234e-2 -> 12.34
+ std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
+ buffer[kk] = '.';
+ if (0 > k + maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[kk + 2]; // Reserve one zero
+ }
+ else
+ return &buffer[length + 1];
+ }
+ else if (-6 < kk && kk <= 0) {
+ // 1234e-6 -> 0.001234
+ const int offset = 2 - kk;
+ std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
+ buffer[0] = '0';
+ buffer[1] = '.';
+ for (int i = 2; i < offset; i++)
+ buffer[i] = '0';
+ if (length - kk > maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = maxDecimalPlaces + 1; i > 2; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[3]; // Reserve one zero
+ }
+ else
+ return &buffer[length + offset];
+ }
+ else if (kk < -maxDecimalPlaces) {
+ // Truncate to zero
+ buffer[0] = '0';
+ return &buffer[1];
+ }
+ else if (length == 1) {
+ // 1e30
+ buffer[1] = 'e';
+ return rapidjson::internal::WriteExponent(kk - 1, &buffer[2]);
+ }
+ else {
+ // 1234e30 -> 1.234e33
+ std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
+ buffer[1] = '.';
+ buffer[length + 1] = 'e';
+ return rapidjson::internal::WriteExponent(kk - 1, &buffer[0 + length + 2]);
+ }
+}
+
+} // namespace
+
+char* dtoa(double value, char* buffer) {
+ rapidjson::internal::Double d(value);
+ if (d.IsZero()) {
+ if (d.Sign())
+ *buffer++ = '-'; // -0.0, Issue #289
+ buffer[0] = '0';
+ return &buffer[1];
+ }
+ else {
+ if (value < 0) {
+ *buffer++ = '-';
+ value = -value;
+ }
+ int length, K;
+ rapidjson::internal::Grisu2(value, buffer, &length, &K);
+ return Prettify(buffer, length, K);
+ }
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/dtoa.hpp b/src/mbgl/util/dtoa.hpp
new file mode 100644
index 0000000000..00c2c25ce2
--- /dev/null
+++ b/src/mbgl/util/dtoa.hpp
@@ -0,0 +1,22 @@
+#ifndef MBGL_UTIL_DTOA
+#define MBGL_UTIL_DTOA
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+char* dtoa(double value, char* buffer);
+
+inline std::string dtoa(double value) {
+ std::string data;
+ data.resize(25);
+ auto end = dtoa(value, const_cast<char*>(data.data()));
+ data.resize(end - data.data());
+ return data;
+}
+
+} // end namespace util
+} // end namespace mbgl
+
+#endif
diff --git a/test/test.gypi b/test/test.gypi
index 5c04262644..51e4a2e621 100644
--- a/test/test.gypi
+++ b/test/test.gypi
@@ -25,6 +25,7 @@
'util/mapbox.cpp',
'util/merge_lines.cpp',
'util/run_loop.cpp',
+ 'util/number_conversions.cpp',
'util/text_conversions.cpp',
'util/thread.cpp',
'util/thread_local.cpp',
diff --git a/test/util/number_conversions.cpp b/test/util/number_conversions.cpp
new file mode 100644
index 0000000000..cfeda3eec7
--- /dev/null
+++ b/test/util/number_conversions.cpp
@@ -0,0 +1,55 @@
+#include <iostream>
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/util/string.hpp>
+
+using namespace mbgl;
+
+TEST(NumberConversions, number_to_string) {
+ EXPECT_EQ("0", util::toString(0));
+ EXPECT_EQ("10", util::toString(10));
+ EXPECT_EQ("-10", util::toString(-10));
+ EXPECT_EQ("10", util::toString(10.0));
+ EXPECT_EQ("-10", util::toString(-10.0));
+
+ EXPECT_EQ("1", util::toString(1));
+ EXPECT_EQ("-1", util::toString(-1));
+ EXPECT_EQ("32768", util::toString(32768));
+ EXPECT_EQ("-32768", util::toString(-32768));
+
+ EXPECT_EQ("1", util::toString(1.000000));
+ EXPECT_EQ("-1", util::toString(-1.000000));
+ EXPECT_EQ("32768", util::toString(32768.000000));
+ EXPECT_EQ("-32768", util::toString(-32768.000000));
+
+ EXPECT_EQ("1.01", util::toString(1.01));
+ EXPECT_EQ("-1.01", util::toString(-1.01));
+ EXPECT_EQ("32768.01", util::toString(32768.01));
+ EXPECT_EQ("-32768.01", util::toString(-32768.01));
+
+ EXPECT_EQ("1.01", util::toString(1.0100));
+ EXPECT_EQ("-1.01", util::toString(-1.0100));
+ EXPECT_EQ("32768.01", util::toString(32768.0100));
+ EXPECT_EQ("-32768.01", util::toString(-32768.0100));
+
+ EXPECT_EQ("1.123456", util::toString(1.123456));
+ EXPECT_EQ("-1.123456", util::toString(-1.123456));
+ EXPECT_EQ("32768.123456", util::toString(32768.123456));
+ EXPECT_EQ("-32768.123456", util::toString(-32768.123456));
+
+ EXPECT_EQ("1.123456789", util::toString(1.123456789));
+ EXPECT_EQ("-1.123456789", util::toString(-1.123456789));
+ EXPECT_EQ("32768.123456789", util::toString(32768.123456789));
+ EXPECT_EQ("-32768.123456789", util::toString(-32768.123456789));
+
+ EXPECT_EQ("3.141592653589793", util::toString(3.1415926535897932385128089594061862044327426701784));
+ EXPECT_EQ("-3.141592653589793", util::toString(-3.1415926535897932385128089594061862044327426701784));
+
+ EXPECT_EQ("123456789", util::toString(123456789));
+
+ EXPECT_EQ("0.0000532", util::toString((long double)5.32e-5));
+ EXPECT_EQ("5.32e-40", util::toString((long double)5.32e-40));
+
+ EXPECT_EQ("10", util::toString(unsigned(10)));
+ EXPECT_EQ("10", util::toString(unsigned(10.0)));
+}