Roll-your-own hacks.
[libdcp.git] / src / raw_convert.cc
index 4736bd960436c866da7d7fa776134f108d2ef261..0984065ce802b82ba502280e0ac19e9621018b26 100644 (file)
@@ -32,8 +32,9 @@
 */
 
 
-#include "raw_convert.h"
+#include "dcp_assert.h"
 #include "locale_convert.h"
+#include "raw_convert.h"
 #include <boost/algorithm/string.hpp>
 
 
@@ -41,91 +42,292 @@ using std::string;
 using std::wstring;
 
 
-/** @param v Numeric value as an ASCII string */
 static
 string
-make_raw (string v)
+make_local (string v)
 {
        struct lconv* lc = localeconv ();
-       /* thousands_sep may be . so remove them before changing decimal points */
-       boost::algorithm::replace_all (v, lc->thousands_sep, "");
-       boost::algorithm::replace_all (v, lc->decimal_point, ".");
+       boost::algorithm::replace_all (v, ".", lc->decimal_point);
+       /* We hope it's ok not to add in thousands separators here */
        return v;
 }
 
 
 static
 string
-make_local (string v)
+int_to_string (uint64_t x)
 {
-       struct lconv* lc = localeconv ();
-       boost::algorithm::replace_all (v, ".", lc->decimal_point);
-       /* We hope it's ok not to add in thousands separators here */
-       return v;
+       if (x == 0) {
+               return "0";
+       }
+
+       uint64_t multiplier = 10;
+       string result = "";
+
+       char const lut[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+
+       while (x) {
+               auto left = x % multiplier;
+               result = lut[left * 10 / multiplier] + result;
+               multiplier *= 10;
+               x -= left;
+       }
+
+       return result;
+}
+
+
+static
+std::pair<string, int>
+double_to_fixed_string(double x, int precision)
+{
+       auto constexpr max_precision = 16;
+       precision = std::min(precision, max_precision);
+
+       char const digit_lut[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+       uint64_t const multiplier_lut[] = {
+               10,  // 0
+               100,  // 1
+               1000,  // 2
+               10000,  // 3
+               100000,  // 4
+               1000000,  // 5
+               10000000,  // 6
+               100000000,  // 7
+               1000000000,  // 8
+               10000000000,  // 9
+               100000000000,  // 10
+               1000000000000,  // 11
+               10000000000000,  // 12
+               100000000000000,  // 13
+               1000000000000000,  // 14
+               10000000000000000   // 15
+       };
+
+       static_assert((sizeof(multiplier_lut) / sizeof(uint64_t)) == max_precision);
+
+       auto const integer_part = static_cast<uint64_t>(x);
+       x -= integer_part;
+
+       string result = int_to_string(integer_part);
+       int decimal_point_position = result.length();
+
+       std::cout << "FIX: " << x << "\n";
+
+       int trailing_zeros = 0;
+       for (int i = 0; i < precision; ++i) {
+               {
+                       auto foo = x * multiplier_lut[i];
+                       int bar;
+                       double normal = frexp(foo, &bar);
+                       std::cout << normal << " " << bar << "\n";
+               }
+
+               std::cout << "=> " << x << " " << (x * multiplier_lut[i]) << " " << std::floor(x * multiplier_lut[i]) << "\n";
+               auto digit = digit_lut[static_cast<uint64_t>(x * multiplier_lut[i]) % 10];
+               if (digit == '0') {
+                       ++trailing_zeros;
+               } else {
+                       trailing_zeros = 0;
+               }
+               result += digit;
+       }
+
+       return { result.substr(0, result.length() - trailing_zeros), decimal_point_position };
+}
+
+
+static
+string
+double_to_string (double x, int precision, bool fixed)
+{
+       string prefix;
+       if (x < 0) {
+               prefix = "-";
+               x = -x;
+       }
+
+       int two_exp;
+       double norm = frexp(x, &two_exp);
+       double ten_exp = two_exp * log10(2);
+       int whole_ten_exp = static_cast<int>(ten_exp);
+       double const correction = pow(10, ten_exp - whole_ten_exp);
+       norm *= correction;
+
+       string result;
+       int decimal_point_position;
+       std::tie(result, decimal_point_position) = double_to_fixed_string(norm, precision);
+       std::cout << "R: " << x << " " << norm << " " << whole_ten_exp << " " << result << " " << decimal_point_position << "\n";
+
+       auto insert_decimal_point = [](string s, int p) {
+               if (p == 0) {
+                       return "0." + s;
+               } else {
+                       return s.substr(0, p) + "." + s.substr(p);
+               }
+       };
+
+       if (!fixed && whole_ten_exp <= -5) {
+               if (whole_ten_exp <= -10) {
+                       return prefix + insert_decimal_point(result, decimal_point_position) + "e-" + int_to_string(-whole_ten_exp);
+               } else {
+                       return prefix + insert_decimal_point(result, decimal_point_position) + "e-0" + int_to_string(-whole_ten_exp);
+               }
+       }
+
+       if (whole_ten_exp < 0) {
+               result = string(-whole_ten_exp, '0') + result;
+       } else {
+               result = result + string(whole_ten_exp, '0');
+       }
+
+       return prefix + insert_decimal_point(result, decimal_point_position);
+
+#if 0
+       string result;
+       if (x < 0) {
+               result = "-";
+               x = -x;
+       }
+
+       int exp;
+       double norm = frexp(x, &exp);
+       std::cout << "woohoo frexp says " << norm << " " << exp << "\n";
+
+       auto constexpr max_precision = 16;
+       precision = std::min(precision, max_precision);
+
+       char const digit_lut[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+       int64_t const multiplier_lut[] = {
+               10,  // 0
+               100,  // 1
+               1000,  // 2
+               10000,  // 3
+               100000,  // 4
+               1000000,  // 5
+               10000000,  // 6
+               100000000,  // 7
+               1000000000,  // 8
+               10000000000,  // 9
+               100000000000,  // 10
+               1000000000000,  // 11
+               10000000000000,  // 12
+               100000000000000,  // 13
+               1000000000000000,  // 14
+               10000000000000000   // 15
+       };
+
+       static_assert((sizeof(multiplier_lut) / sizeof(int64_t)) == max_precision);
+
+       int exponent = 0;
+       if (!fixed && x < 0.0001) {
+               while ((x * multiplier_lut[-exponent + 1]) < 1.0) {
+                       --exponent;
+               }
+       }
+
+       x *= multiplier_lut[-exponent + 1];
+
+       auto const integer_part = static_cast<uint64_t>(x);
+
+       result += int_to_string(integer_part) + ".";
+       x -= integer_part;
+
+       bool leading = true;
+       int leading_zeros = 0;
+       int trailing_zeros = 0;
+       string fractional_part;
+       for (int i = 0; i < precision; ++i) {
+               auto digit = digit_lut[static_cast<int64_t>(x * multiplier_lut[i]) % 10];
+               if (digit == '0') {
+                       if (leading) {
+                               ++leading_zeros;
+                       } else {
+                               ++trailing_zeros;
+                       }
+               } else {
+                       leading = false;
+                       trailing_zeros = 0;
+               }
+               result += digit;
+       }
+
+       result = result.substr(0, result.length() - trailing_zeros);
+
+       if (exponent < 0) {
+               if (exponent <= -10) {
+                       result += "e-" + int_to_string(-exponent);
+               } else {
+                       result == "e-0" + int_to_string(-exponent);
+               }
+       }
+
+       return result;
+#endif
 }
 
 
 template <>
 string
-dcp::raw_convert (unsigned char v, int precision, bool fixed)
+dcp::raw_convert (unsigned char v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return int_to_string(static_cast<int64_t>(v));
 }
 
 
 template <>
 string
-dcp::raw_convert (unsigned short int v, int precision, bool fixed)
+dcp::raw_convert (unsigned short int v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return int_to_string(v);
 }
 
 
 template <>
 string
-dcp::raw_convert (int v, int precision, bool fixed)
+dcp::raw_convert (int v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return v >= 0 ? int_to_string(v) : "-" + int_to_string(-v);
 }
 
 
 template <>
 string
-dcp::raw_convert (unsigned int v, int precision, bool fixed)
+dcp::raw_convert (unsigned int v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return int_to_string(v);
 }
 
 
 template <>
 string
-dcp::raw_convert (long v, int precision, bool fixed)
+dcp::raw_convert (long v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return v >= 0 ? int_to_string(v) : "-" + int_to_string(-v);
 }
 
 
 template <>
 string
-dcp::raw_convert (unsigned long v, int precision, bool fixed)
+dcp::raw_convert (unsigned long v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return int_to_string(v);
 }
 
 
 template <>
 string
-dcp::raw_convert (long long v, int precision, bool fixed)
+dcp::raw_convert (long long v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return v >= 0 ? int_to_string(v) : "-" + int_to_string(-v);
 }
 
 
 template <>
 string
-dcp::raw_convert (unsigned long long v, int precision, bool fixed)
+dcp::raw_convert (unsigned long long v, int, bool)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return int_to_string(v);
 }
 
 
@@ -133,7 +335,7 @@ template <>
 string
 dcp::raw_convert (float v, int precision, bool fixed)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return double_to_string(v, precision, fixed);
 }
 
 
@@ -141,7 +343,7 @@ template <>
 string
 dcp::raw_convert (double v, int precision, bool fixed)
 {
-       return make_raw (locale_convert<string> (v, precision, fixed));
+       return double_to_string(v, precision, fixed);
 }