Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_PrintDouble.cpp
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #include "Teuchos_PrintDouble.hpp"
11 #include "Teuchos_BigUInt.hpp"
12 
13 #include <cstring>
14 
15 namespace Teuchos {
16 
17 namespace {
18 
19 int ndigits_for(std::uint32_t x) {
20  int n = 0;
21  while (x) {
22  ++n;
23  x /= 10;
24  }
25  return n;
26 }
27 
28 }
29 
65 void print_double(std::ostream& os, double v) {
66  char buffer[64];
67  constexpr std::uint64_t one = 1;
68  constexpr std::uint64_t p = 53;
69  std::uint64_t pun;
70  std::memcpy(&pun, &v, sizeof(v));
71  auto sign = pun >> 63;
72  pun -= sign << 63;
73  auto be = pun >> (p - 1);
74  pun -= be << (p - 1);
75  auto m = pun;
76  int bp = 0;
77  if (be == 2047) {
78  if (m == 0) {
79  if (sign) buffer[bp++] = '-';
80  buffer[bp++] = 'i';
81  buffer[bp++] = 'n';
82  buffer[bp++] = 'f';
83  } else {
84  buffer[bp++] = 'n';
85  buffer[bp++] = 'a';
86  buffer[bp++] = 'n';
87  }
88  } else {
89  if (sign) buffer[bp++] = '-';
90  std::uint64_t f;
91  if (be == 0) {
92  f = m;
93  } else {
94  f = m + (one << (p - 1));
95  }
96  auto e = std::int64_t(be) - 1075;
97  BigUInt<34> r, s, mp, mm;
98  if (e >= 0) {
99  if (f != (one << (p - 1))) {
100  r = BigUInt<34>(f);
101  r <<= (e + 1);
102  s = BigUInt<34>(2);
103  mp = BigUInt<34>(1);
104  mp <<= e;
105  mm = BigUInt<34>(1);
106  mm <<= e;
107  } else {
108  r = BigUInt<34>(f);
109  r <<= (e + 2);
110  s = BigUInt<34>(2 * 2);
111  mp = BigUInt<34>(1);
112  mp <<= (e + 1);
113  mm = BigUInt<34>(1);
114  mm <<= e;
115  }
116  } else {
117  if ((be == 0) || (f != (one << (p - 1)))) {
118  r = BigUInt<34>(f);
119  r <<= 1;
120  s = BigUInt<34>(1);
121  s <<= (1 - e);
122  mp = BigUInt<34>(1);
123  mm = BigUInt<34>(1);
124  } else {
125  r = BigUInt<34>(f);
126  r <<= 2;
127  s = BigUInt<34>(1);
128  s <<= (2 - e);
129  mp = BigUInt<34>(2);
130  mm = BigUInt<34>(1);
131  }
132  }
133  std::int32_t k = 0;
134  BigUInt<34> B_k{1};
135  auto r_p_mp = r + mp;
136  auto r_p_mp_comp = comp(r_p_mp, s);
137  if (r_p_mp_comp == 0) {
138  } else if (r_p_mp_comp == 1) {
139  while (r_p_mp > (s * B_k)) {
140  ++k, B_k *= 10;
141  }
142  } else {
143  while ((r_p_mp * B_k) < s) {
144  --k, B_k *= 10;
145  }
146  ++k;
147  B_k = B_k / 10;
148  }
149  if (k >= 0) {
150  s = s * B_k;
151  } else {
152  r = r * B_k;
153  mp = mp * B_k;
154  mm = mm * B_k;
155  }
156  char last_d = '0';
157  int n;
158  for (n = 0; true; ++n) {
159  auto r_x_10 = r;
160  r_x_10 *= 10;
161  auto d_np1 = r_x_10 / s;
162  auto cond1 = r < mm;
163  auto cond2 = (r + mp) > s;
164  if (cond1 && cond2) {
165  r <<= 1;
166  if (r < s) {
167  buffer[bp++] = last_d;
168  } else {
169  buffer[bp++] = last_d + 1;
170  }
171  break;
172  } else if (cond1) {
173  buffer[bp++] = last_d;
174  break;
175  } else if (cond2) {
176  buffer[bp++] = last_d + 1;
177  break;
178  } else {
179  if (n) buffer[bp++] = last_d;
180  r = r_x_10;
181  r -= (s * d_np1);
182  mp *= 10;
183  mm *= 10;
184  last_d = char(d_np1[0]) + '0';
185  }
186  }
187  if (v == 0.0) {
188  k = 1;
189  ++n;
190  }
191  int dot_pos = -1;
192  bool do_scientific = false;
193  if (0 <= k && k <= n) {
194  // dot is touching significant digits
195  dot_pos = k;
196  } else if (k < 0) {
197  auto nchars_sci = ndigits_for(-k + 1) + 2;
198  if (n > 1) nchars_sci += 1; // add a dot to scientific notation if more than one digit
199  if (nchars_sci < (-k + 1)) {
200  // scientific notation requires fewer chars than trailing zeros
201  if (n > 1) dot_pos = 1;
202  do_scientific = true;
203  } else {
204  // trailing zeros are no more chars than scientific
205  for (int i = 0; i < n; ++i) {
206  buffer[bp + (-k) - i - 1] = buffer[bp - i - 1];
207  }
208  for (int i = 0; i < -k; ++i) {
209  buffer[bp - n + i] = '0';
210  }
211  dot_pos = bp - n;
212  bp += -k;
213  n += -k;
214  }
215  } else if (k > n) {
216  auto nchars_sci = ndigits_for(k - 1) + 1;
217  if (n > 1) nchars_sci += 1; // add a dot to scientific notation if more than one digit
218  if (nchars_sci < ((k-n)+1)) {
219  // scientific notation requires fewer chars than trailing zeros
220  if (n > 1) dot_pos = 1;
221  do_scientific = true;
222  } else {
223  // trailing zeros are no more chars than scientific
224  for (; n < k; ++n) buffer[bp++] = '0';
225  dot_pos = n;
226  }
227  }
228  if (dot_pos != -1) {
229  for (int i = 0; i < (n - dot_pos) && i < bp; ++i) buffer[bp - i] = buffer[bp - i - 1];
230  buffer[bp - n + dot_pos] = '.';
231  ++bp;
232  }
233  if (do_scientific) {
234  buffer[bp++] = 'e';
235  auto decimal_exponent = (k - 1);
236  if (decimal_exponent < 0) {
237  buffer[bp++] = '-';
238  decimal_exponent = -decimal_exponent;
239  }
240  int ne;
241  for (ne = 0; decimal_exponent; ++ne) {
242  buffer[bp++] = char(decimal_exponent % 10) + '0';
243  decimal_exponent /= 10;
244  }
245  for (int i = 0; i < ne / 2; ++i) {
246  auto tmp = buffer[bp - ne + i];
247  buffer[bp - ne + i] = buffer[bp - i - 1];
248  buffer[bp - i - 1] = tmp;
249  }
250  }
251  }
252  buffer[bp] = '\0';
253  os << buffer;
254 }
255 
256 }
void print_double(std::ostream &os, double v)
Prints a double-precision floating-point number exactly using minimal characters. ...
Arbitrary-precision unsigned integer class.
Arbitrary-precision unsigned integer definition.
Declares Teuchos::print_double.