float_string_cvt.hpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. ///////////////////////////////////////////////////////////////
  2. // Copyright 2013 John Maddock. Distributed under the Boost
  3. // Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
  5. //
  6. // Generic routines for converting floating point values to and from decimal strings.
  7. // Note that these use "naive" algorithms which result in rounding error - so they
  8. // do not round trip to and from the string representation (but should only be out
  9. // in the last bit).
  10. //
  11. #ifndef BOOST_MP_FLOAT_STRING_CVT_HPP
  12. #define BOOST_MP_FLOAT_STRING_CVT_HPP
  13. #include <string>
  14. #include <cctype>
  15. #include <boost/multiprecision/detail/no_exceptions_support.hpp>
  16. #include <boost/multiprecision/detail/assert.hpp>
  17. namespace boost { namespace multiprecision { namespace detail {
  18. template <class I>
  19. inline void round_string_up_at(std::string& s, std::ptrdiff_t pos, I& expon)
  20. {
  21. //
  22. // Rounds up a string representation of a number at pos:
  23. //
  24. if (pos < 0)
  25. {
  26. s.insert(static_cast<std::string::size_type>(0), 1, '1');
  27. s.erase(s.size() - 1);
  28. ++expon;
  29. }
  30. else if (s[static_cast<std::size_t>(pos)] == '9')
  31. {
  32. s[static_cast<std::size_t>(pos)] = '0';
  33. round_string_up_at(s, pos - 1, expon);
  34. }
  35. else
  36. {
  37. if ((pos == 0) && (s[static_cast<std::size_t>(pos)] == '0') && (s.size() == 1))
  38. ++expon;
  39. ++s[static_cast<std::size_t>(pos)];
  40. }
  41. }
  42. template <class Backend>
  43. std::string convert_to_string(Backend b, std::streamsize digits, std::ios_base::fmtflags f)
  44. {
  45. using default_ops::eval_convert_to;
  46. using default_ops::eval_divide;
  47. using default_ops::eval_floor;
  48. using default_ops::eval_fpclassify;
  49. using default_ops::eval_log10;
  50. using default_ops::eval_multiply;
  51. using default_ops::eval_pow;
  52. using default_ops::eval_subtract;
  53. using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type;
  54. using exponent_type = typename Backend::exponent_type ;
  55. std::string result;
  56. bool iszero = false;
  57. bool isneg = false;
  58. exponent_type expon = 0;
  59. std::streamsize org_digits = digits;
  60. BOOST_MP_ASSERT(digits > 0);
  61. int fpt = eval_fpclassify(b);
  62. if (fpt == static_cast<int>(FP_ZERO))
  63. {
  64. result = "0";
  65. iszero = true;
  66. }
  67. else if (fpt == static_cast<int>(FP_INFINITE))
  68. {
  69. if (b.compare(ui_type(0)) < 0)
  70. return "-inf";
  71. else
  72. return ((f & std::ios_base::showpos) == std::ios_base::showpos) ? "+inf" : "inf";
  73. }
  74. else if (fpt == static_cast<int>(FP_NAN))
  75. {
  76. return "nan";
  77. }
  78. else
  79. {
  80. //
  81. // Start by figuring out the exponent:
  82. //
  83. isneg = b.compare(ui_type(0)) < 0;
  84. if (isneg)
  85. b.negate();
  86. Backend t;
  87. Backend ten;
  88. ten = ui_type(10);
  89. eval_log10(t, b);
  90. eval_floor(t, t);
  91. eval_convert_to(&expon, t);
  92. if (-expon > std::numeric_limits<number<Backend> >::max_exponent10 - 3)
  93. {
  94. int e = -expon / 2;
  95. Backend t2;
  96. eval_pow(t2, ten, e);
  97. eval_multiply(t, t2, b);
  98. eval_multiply(t, t2);
  99. if (expon & 1)
  100. eval_multiply(t, ten);
  101. }
  102. else
  103. {
  104. eval_pow(t, ten, -expon);
  105. eval_multiply(t, b);
  106. }
  107. //
  108. // Make sure we're between [1,10) and adjust if not:
  109. //
  110. if (t.compare(ui_type(1)) < 0)
  111. {
  112. eval_multiply(t, ui_type(10));
  113. --expon;
  114. }
  115. else if (t.compare(ui_type(10)) >= 0)
  116. {
  117. eval_divide(t, ui_type(10));
  118. ++expon;
  119. }
  120. Backend digit;
  121. ui_type cdigit;
  122. //
  123. // Adjust the number of digits required based on formatting options:
  124. //
  125. if (((f & std::ios_base::fixed) == std::ios_base::fixed) && (expon != -1))
  126. digits += expon + 1;
  127. if ((f & std::ios_base::scientific) == std::ios_base::scientific)
  128. ++digits;
  129. //
  130. // Extract the digits one at a time:
  131. //
  132. for (unsigned i = 0; i < digits; ++i)
  133. {
  134. eval_floor(digit, t);
  135. eval_convert_to(&cdigit, digit);
  136. result += static_cast<char>('0' + cdigit);
  137. eval_subtract(t, digit);
  138. eval_multiply(t, ten);
  139. }
  140. //
  141. // Possibly round result:
  142. //
  143. if (digits >= 0)
  144. {
  145. eval_floor(digit, t);
  146. eval_convert_to(&cdigit, digit);
  147. eval_subtract(t, digit);
  148. if ((cdigit == 5) && (t.compare(ui_type(0)) == 0))
  149. {
  150. // Bankers rounding:
  151. if ((*result.rbegin() - '0') & 1)
  152. {
  153. round_string_up_at(result, static_cast<std::ptrdiff_t>(result.size() - 1u), expon);
  154. }
  155. }
  156. else if (cdigit >= 5)
  157. {
  158. round_string_up_at(result, static_cast<std::ptrdiff_t>(result.size() - 1u), expon);
  159. }
  160. }
  161. eval_floor(t, b);
  162. if ((t.compare(b) == 0) && (static_cast<std::size_t>(expon + 1) < result.size()))
  163. {
  164. // Input is an integer, sometimes we get a result which is not an integer here as a result of printing too
  165. // many digits, so lets round if required:
  166. round_string_up_at(result, expon + 1, expon);
  167. result.erase(static_cast<std::string::size_type>(expon + 1));
  168. }
  169. }
  170. while ((static_cast<std::streamsize>(result.size()) > digits) && (result.size() != 0U))
  171. {
  172. // We may get here as a result of rounding...
  173. if (result.size() > 1)
  174. result.erase(result.size() - 1);
  175. else
  176. {
  177. if (expon > 0)
  178. --expon; // so we put less padding in the result.
  179. else
  180. ++expon;
  181. ++digits;
  182. }
  183. }
  184. BOOST_MP_ASSERT(org_digits >= 0);
  185. if (isneg)
  186. result.insert(static_cast<std::string::size_type>(0), 1, '-');
  187. format_float_string(result, expon, org_digits, f, iszero);
  188. return result;
  189. }
  190. template <class Backend>
  191. void convert_from_string(Backend& b, const char* p)
  192. {
  193. using default_ops::eval_add;
  194. using default_ops::eval_divide;
  195. using default_ops::eval_multiply;
  196. using default_ops::eval_pow;
  197. using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type;
  198. b = ui_type(0);
  199. if (!p || (*p == 0))
  200. return;
  201. bool is_neg = false;
  202. bool is_neg_expon = false;
  203. constexpr ui_type ten = ui_type(10);
  204. typename Backend::exponent_type expon = 0;
  205. int digits_seen = 0;
  206. using limits = std::numeric_limits<number<Backend, et_off>>;
  207. constexpr int max_digits = limits::is_specialized ? limits::max_digits10 + 1 : INT_MAX;
  208. if (*p == '+')
  209. ++p;
  210. else if (*p == '-')
  211. {
  212. is_neg = true;
  213. ++p;
  214. }
  215. if ((std::strcmp(p, "nan") == 0) || (std::strcmp(p, "NaN") == 0) || (std::strcmp(p, "NAN") == 0))
  216. {
  217. eval_divide(b, ui_type(0));
  218. if (is_neg)
  219. b.negate();
  220. return;
  221. }
  222. if ((std::strcmp(p, "inf") == 0) || (std::strcmp(p, "Inf") == 0) || (std::strcmp(p, "INF") == 0))
  223. {
  224. b = ui_type(1);
  225. eval_divide(b, ui_type(0));
  226. if (is_neg)
  227. b.negate();
  228. return;
  229. }
  230. //
  231. // Grab all the leading digits before the decimal point:
  232. //
  233. while (std::isdigit(*p))
  234. {
  235. eval_multiply(b, ten);
  236. eval_add(b, ui_type(*p - '0'));
  237. ++p;
  238. ++digits_seen;
  239. }
  240. if (*p == '.')
  241. {
  242. //
  243. // Grab everything after the point, stop when we've seen
  244. // enough digits, even if there are actually more available:
  245. //
  246. ++p;
  247. while (std::isdigit(*p))
  248. {
  249. eval_multiply(b, ten);
  250. eval_add(b, ui_type(*p - '0'));
  251. ++p;
  252. --expon;
  253. if (++digits_seen > max_digits)
  254. break;
  255. }
  256. while (std::isdigit(*p))
  257. ++p;
  258. }
  259. //
  260. // Parse the exponent:
  261. //
  262. if ((*p == 'e') || (*p == 'E'))
  263. {
  264. ++p;
  265. if (*p == '+')
  266. ++p;
  267. else if (*p == '-')
  268. {
  269. is_neg_expon = true;
  270. ++p;
  271. }
  272. typename Backend::exponent_type e2 = 0;
  273. while (std::isdigit(*p))
  274. {
  275. e2 *= 10;
  276. e2 += (*p - '0');
  277. ++p;
  278. }
  279. if (is_neg_expon)
  280. e2 = -e2;
  281. expon += e2;
  282. }
  283. if (expon)
  284. {
  285. // Scale by 10^expon, note that 10^expon can be
  286. // outside the range of our number type, even though the
  287. // result is within range, if that looks likely, then split
  288. // the calculation in two:
  289. Backend t;
  290. t = ten;
  291. if (expon > limits::min_exponent10 + 2)
  292. {
  293. eval_pow(t, t, expon);
  294. eval_multiply(b, t);
  295. }
  296. else
  297. {
  298. eval_pow(t, t, expon + digits_seen + 1);
  299. eval_multiply(b, t);
  300. t = ten;
  301. eval_pow(t, t, -digits_seen - 1);
  302. eval_multiply(b, t);
  303. }
  304. }
  305. if (is_neg)
  306. b.negate();
  307. if (*p)
  308. {
  309. // Unexpected input in string:
  310. BOOST_MP_THROW_EXCEPTION(std::runtime_error("Unexpected characters in string being interpreted as a float128."));
  311. }
  312. }
  313. }}} // namespace boost::multiprecision::detail
  314. #endif