/* Copyright (C) 2014-2021 Carl Hetherington This file is part of libdcp. libdcp is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. libdcp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libdcp. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "dcp_assert.h" #include "locale_convert.h" #include "raw_convert.h" #include using std::string; using std::wstring; static string make_local (string v) { 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; } static string int_to_string (uint64_t x) { 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 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(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(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(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(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(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, bool) { return int_to_string(static_cast(v)); } template <> string dcp::raw_convert (unsigned short int v, int, bool) { return int_to_string(v); } template <> string dcp::raw_convert (int v, int, bool) { return v >= 0 ? int_to_string(v) : "-" + int_to_string(-v); } template <> string dcp::raw_convert (unsigned int v, int, bool) { return int_to_string(v); } template <> string dcp::raw_convert (long v, int, bool) { return v >= 0 ? int_to_string(v) : "-" + int_to_string(-v); } template <> string dcp::raw_convert (unsigned long v, int, bool) { return int_to_string(v); } template <> string dcp::raw_convert (long long v, int, bool) { return v >= 0 ? int_to_string(v) : "-" + int_to_string(-v); } template <> string dcp::raw_convert (unsigned long long v, int, bool) { return int_to_string(v); } template <> string dcp::raw_convert (float v, int precision, bool fixed) { return double_to_string(v, precision, fixed); } template <> string dcp::raw_convert (double v, int precision, bool fixed) { return double_to_string(v, precision, fixed); } template <> string dcp::raw_convert (char const * v, int, bool) { return v; } template <> string dcp::raw_convert (char* v, int, bool) { return v; } template <> string dcp::raw_convert (string v, int, bool) { return v; } template <> string dcp::raw_convert (char v, int, bool) { string s; s += v; return s; } template <> unsigned char dcp::raw_convert(string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> unsigned short int dcp::raw_convert(string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> int dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> long dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> unsigned long dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> long long dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> unsigned long long dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> int dcp::raw_convert(char* v, int precision, bool fixed) { return locale_convert(make_local (v), precision, fixed); } template <> int dcp::raw_convert (char const * v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> float dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> float dcp::raw_convert (char const * v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> double dcp::raw_convert (string v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); } template <> double dcp::raw_convert (char const * v, int precision, bool fixed) { return locale_convert (make_local (v), precision, fixed); }