Teuchos - Trilinos Tools Package Version of the Day
Loading...
Searching...
No Matches
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
11#include "Teuchos_BigUInt.hpp"
12
13#include <cstring>
14
15namespace Teuchos {
16
17namespace {
18
19int 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
65void 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++] = '-';
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}
Arbitrary-precision unsigned integer definition.
Declares Teuchos::print_double.
Smart reference counting pointer class for automatic garbage collection.
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos,...
void print_double(std::ostream &os, double v)
Prints a double-precision floating-point number exactly using minimal characters.