d: Merge upstream dmd 568496d5b, druntime 178c44ff, phobos 574bf883b.
[official-gcc.git] / libphobos / src / std / format / internal / floats.d
blob632bf7674a67202845efb32b3e2dc2165da018c0
1 // Written in the D programming language.
3 /*
4 Helper functions for formatting floating point numbers.
6 Copyright: Copyright The D Language Foundation 2019 -
8 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
10 Authors: Bernhard Seckinger
12 Source: $(PHOBOSSRC std/format/internal/floats.d)
15 module std.format.internal.floats;
17 import std.format.spec : FormatSpec;
19 // wrapper for unittests
20 private auto printFloat(T, Char)(const(T) val, FormatSpec!Char f)
21 if (is(T == float) || is(T == double)
22 || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
24 import std.array : appender;
25 auto w = appender!string();
27 printFloat(w, val, f);
28 return w.data;
31 package(std.format) void printFloat(Writer, T, Char)(auto ref Writer w, const(T) val, FormatSpec!Char f)
32 if (is(T == float) || is(T == double)
33 || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
35 import std.math.operations : extractBitpattern, FloatingPointBitpattern;
37 auto bp = extractBitpattern(val);
39 ulong mnt = bp.mantissa;
40 int exp = bp.exponent;
41 string sgn = bp.negative ? "-" : "";
43 if (sgn == "" && f.flPlus) sgn = "+";
44 if (sgn == "" && f.flSpace) sgn = " ";
46 assert(f.spec == 'a' || f.spec == 'A'
47 || f.spec == 'e' || f.spec == 'E'
48 || f.spec == 'f' || f.spec == 'F'
49 || f.spec == 'g' || f.spec == 'G', "unsupported format specifier");
50 bool is_upper = f.spec == 'A' || f.spec == 'E' || f.spec=='F' || f.spec=='G';
52 // special treatment for nan and inf
53 if (exp == T.max_exp)
55 import std.format.internal.write : writeAligned;
57 f.flZero = false;
58 writeAligned(w, sgn, "", (mnt == 0) ? ( is_upper ? "INF" : "inf" ) : ( is_upper ? "NAN" : "nan" ), f);
59 return;
62 final switch (f.spec)
64 case 'a': case 'A':
65 printFloatA(w, val, f, sgn, exp, mnt, is_upper);
66 break;
67 case 'e': case 'E':
68 printFloatE!false(w, val, f, sgn, exp, mnt, is_upper);
69 break;
70 case 'f': case 'F':
71 printFloatF!false(w, val, f, sgn, exp, mnt, is_upper);
72 break;
73 case 'g': case 'G':
74 printFloatG(w, val, f, sgn, exp, mnt, is_upper);
75 break;
79 private void printFloatA(Writer, T, Char)(auto ref Writer w, const(T) val,
80 FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
81 if (is(T == float) || is(T == double)
82 || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
84 import std.algorithm.comparison : max;
85 import std.format.internal.write : writeAligned, PrecisionType;
87 char[3] prefix;
88 if (sgn != "") prefix[0] = sgn[0];
89 prefix[1] = '0';
90 prefix[2] = is_upper ? 'X' : 'x';
92 // print exponent
93 if (mnt == 0)
95 if (f.precision == f.UNSPECIFIED)
96 f.precision = 0;
97 writeAligned(w, prefix[1 - sgn.length .. $], "0", ".", is_upper ? "P+0" : "p+0",
98 f, PrecisionType.fractionalDigits);
99 return;
102 // save integer part
103 char first = '0' + ((mnt >> (T.mant_dig - 1)) & 1);
104 mnt &= (1L << (T.mant_dig - 1)) - 1;
106 static if (is(T == float) || (is(T == real) && T.mant_dig == 64))
108 mnt <<= 1; // make mnt dividable by 4
109 enum mant_len = T.mant_dig;
111 else
112 enum mant_len = T.mant_dig - 1;
113 static assert(mant_len % 4 == 0, "mantissa with wrong length");
115 // print full mantissa
116 char[(mant_len - 1) / 4 + 3] hex_mant;
117 size_t hex_mant_pos = 2;
118 size_t pos = mant_len;
120 auto gap = 39 - 32 * is_upper;
121 while (pos >= 4 && (mnt & (((1L << (pos - 1)) - 1) << 1) + 1) != 0)
123 pos -= 4;
124 size_t tmp = (mnt >> pos) & 15;
125 // For speed reasons the better readable
126 // ... = tmp < 10 ? ('0' + tmp) : ((is_upper ? 'A' : 'a') + tmp - 10))
127 // has been replaced with an expression without branches, doing the same
128 hex_mant[hex_mant_pos++] = cast(char) (tmp + gap * ((tmp + 6) >> 4) + '0');
130 hex_mant[0] = first;
131 hex_mant[1] = '.';
133 if (f.precision == f.UNSPECIFIED)
134 f.precision = cast(int) hex_mant_pos - 2;
136 auto exp_sgn = exp >= 0 ? '+' : '-';
137 if (exp < 0) exp = -exp;
139 static if (is(T == real) && real.mant_dig == 64)
140 enum max_exp_digits = 8;
141 else static if (is(T == float))
142 enum max_exp_digits = 5;
143 else
144 enum max_exp_digits = 6;
146 char[max_exp_digits] exp_str;
147 size_t exp_pos = max_exp_digits;
151 exp_str[--exp_pos] = '0' + exp % 10;
152 exp /= 10;
153 } while (exp > 0);
155 exp_str[--exp_pos] = exp_sgn;
156 exp_str[--exp_pos] = is_upper ? 'P' : 'p';
158 if (f.precision < hex_mant_pos - 2)
160 import std.format.internal.write : RoundingClass, round;
162 RoundingClass rc;
164 if (hex_mant[f.precision + 2] == '0')
165 rc = RoundingClass.ZERO;
166 else if (hex_mant[f.precision + 2] < '8')
167 rc = RoundingClass.LOWER;
168 else if (hex_mant[f.precision + 2] > '8')
169 rc = RoundingClass.UPPER;
170 else
171 rc = RoundingClass.FIVE;
173 if (rc == RoundingClass.ZERO || rc == RoundingClass.FIVE)
175 foreach (i;f.precision + 3 .. hex_mant_pos)
177 if (hex_mant[i] > '0')
179 rc = rc == RoundingClass.ZERO ? RoundingClass.LOWER : RoundingClass.UPPER;
180 break;
185 hex_mant_pos = f.precision + 2;
187 round(hex_mant, 0, hex_mant_pos, rc, sgn == "-", is_upper ? 'F' : 'f');
190 writeAligned(w, prefix[1 - sgn.length .. $], hex_mant[0 .. 1], hex_mant[1 .. hex_mant_pos],
191 exp_str[exp_pos .. $], f, PrecisionType.fractionalDigits);
194 @safe unittest
196 auto f = FormatSpec!dchar("");
197 f.spec = 'a';
198 assert(printFloat(float.nan, f) == "nan");
199 assert(printFloat(-float.nan, f) == "-nan");
200 assert(printFloat(float.infinity, f) == "inf");
201 assert(printFloat(-float.infinity, f) == "-inf");
202 assert(printFloat(0.0f, f) == "0x0p+0");
203 assert(printFloat(-0.0f, f) == "-0x0p+0");
205 assert(printFloat(double.nan, f) == "nan");
206 assert(printFloat(-double.nan, f) == "-nan");
207 assert(printFloat(double.infinity, f) == "inf");
208 assert(printFloat(-double.infinity, f) == "-inf");
209 assert(printFloat(0.0, f) == "0x0p+0");
210 assert(printFloat(-0.0, f) == "-0x0p+0");
212 static if (real.mant_dig > 64)
214 pragma(msg, "printFloat tests disabled because of unsupported `real` format");
216 else
218 assert(printFloat(real.nan, f) == "nan");
219 assert(printFloat(-real.nan, f) == "-nan");
220 assert(printFloat(real.infinity, f) == "inf");
221 assert(printFloat(-real.infinity, f) == "-inf");
222 assert(printFloat(0.0L, f) == "0x0p+0");
223 assert(printFloat(-0.0L, f) == "-0x0p+0");
226 import std.math.operations : nextUp;
228 assert(printFloat(nextUp(0.0f), f) == "0x0.000002p-126");
229 assert(printFloat(float.epsilon, f) == "0x1p-23");
230 assert(printFloat(float.min_normal, f) == "0x1p-126");
231 assert(printFloat(float.max, f) == "0x1.fffffep+127");
233 assert(printFloat(nextUp(0.0), f) == "0x0.0000000000001p-1022");
234 assert(printFloat(double.epsilon, f) == "0x1p-52");
235 assert(printFloat(double.min_normal, f) == "0x1p-1022");
236 assert(printFloat(double.max, f) == "0x1.fffffffffffffp+1023");
238 static if (real.mant_dig == 64)
240 assert(printFloat(nextUp(0.0L), f) == "0x0.0000000000000002p-16382");
241 assert(printFloat(real.epsilon, f) == "0x1p-63");
242 assert(printFloat(real.min_normal, f) == "0x1p-16382");
243 assert(printFloat(real.max, f) == "0x1.fffffffffffffffep+16383");
246 import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
247 LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
249 assert(printFloat(cast(float) E, f) == "0x1.5bf0a8p+1");
250 assert(printFloat(cast(float) PI, f) == "0x1.921fb6p+1");
251 assert(printFloat(cast(float) PI_2, f) == "0x1.921fb6p+0");
252 assert(printFloat(cast(float) PI_4, f) == "0x1.921fb6p-1");
253 assert(printFloat(cast(float) M_1_PI, f) == "0x1.45f306p-2");
254 assert(printFloat(cast(float) M_2_PI, f) == "0x1.45f306p-1");
255 assert(printFloat(cast(float) M_2_SQRTPI, f) == "0x1.20dd76p+0");
256 assert(printFloat(cast(float) LN10, f) == "0x1.26bb1cp+1");
257 assert(printFloat(cast(float) LN2, f) == "0x1.62e43p-1");
258 assert(printFloat(cast(float) LOG2, f) == "0x1.344136p-2");
259 assert(printFloat(cast(float) LOG2E, f) == "0x1.715476p+0");
260 assert(printFloat(cast(float) LOG2T, f) == "0x1.a934fp+1");
261 assert(printFloat(cast(float) LOG10E, f) == "0x1.bcb7b2p-2");
262 assert(printFloat(cast(float) SQRT2, f) == "0x1.6a09e6p+0");
263 assert(printFloat(cast(float) SQRT1_2, f) == "0x1.6a09e6p-1");
265 assert(printFloat(cast(double) E, f) == "0x1.5bf0a8b145769p+1");
266 assert(printFloat(cast(double) PI, f) == "0x1.921fb54442d18p+1");
267 assert(printFloat(cast(double) PI_2, f) == "0x1.921fb54442d18p+0");
268 assert(printFloat(cast(double) PI_4, f) == "0x1.921fb54442d18p-1");
269 assert(printFloat(cast(double) M_1_PI, f) == "0x1.45f306dc9c883p-2");
270 assert(printFloat(cast(double) M_2_PI, f) == "0x1.45f306dc9c883p-1");
271 assert(printFloat(cast(double) M_2_SQRTPI, f) == "0x1.20dd750429b6dp+0");
272 assert(printFloat(cast(double) LN10, f) == "0x1.26bb1bbb55516p+1");
273 assert(printFloat(cast(double) LN2, f) == "0x1.62e42fefa39efp-1");
274 assert(printFloat(cast(double) LOG2, f) == "0x1.34413509f79ffp-2");
275 assert(printFloat(cast(double) LOG2E, f) == "0x1.71547652b82fep+0");
276 assert(printFloat(cast(double) LOG2T, f) == "0x1.a934f0979a371p+1");
277 assert(printFloat(cast(double) LOG10E, f) == "0x1.bcb7b1526e50ep-2");
278 assert(printFloat(cast(double) SQRT2, f) == "0x1.6a09e667f3bcdp+0");
279 assert(printFloat(cast(double) SQRT1_2, f) == "0x1.6a09e667f3bcdp-1");
281 static if (real.mant_dig == 64)
283 assert(printFloat(E, f) == "0x1.5bf0a8b145769536p+1");
284 assert(printFloat(PI, f) == "0x1.921fb54442d1846ap+1");
285 assert(printFloat(PI_2, f) == "0x1.921fb54442d1846ap+0");
286 assert(printFloat(PI_4, f) == "0x1.921fb54442d1846ap-1");
287 assert(printFloat(M_1_PI, f) == "0x1.45f306dc9c882a54p-2");
288 assert(printFloat(M_2_PI, f) == "0x1.45f306dc9c882a54p-1");
289 assert(printFloat(M_2_SQRTPI, f) == "0x1.20dd750429b6d11ap+0");
290 assert(printFloat(LN10, f) == "0x1.26bb1bbb5551582ep+1");
291 assert(printFloat(LN2, f) == "0x1.62e42fefa39ef358p-1");
292 assert(printFloat(LOG2, f) == "0x1.34413509f79fef32p-2");
293 assert(printFloat(LOG2E, f) == "0x1.71547652b82fe178p+0");
294 assert(printFloat(LOG2T, f) == "0x1.a934f0979a3715fcp+1");
295 assert(printFloat(LOG10E, f) == "0x1.bcb7b1526e50e32ap-2");
296 assert(printFloat(SQRT2, f) == "0x1.6a09e667f3bcc908p+0");
297 assert(printFloat(SQRT1_2, f) == "0x1.6a09e667f3bcc908p-1");
301 @safe unittest
303 auto f = FormatSpec!dchar("");
304 f.spec = 'a';
305 f.precision = 3;
307 assert(printFloat(1.0f, f) == "0x1.000p+0");
308 assert(printFloat(3.3f, f) == "0x1.a66p+1");
309 assert(printFloat(2.9f, f) == "0x1.733p+1");
311 assert(printFloat(1.0, f) == "0x1.000p+0");
312 assert(printFloat(3.3, f) == "0x1.a66p+1");
313 assert(printFloat(2.9, f) == "0x1.733p+1");
315 static if (real.mant_dig == 64)
317 assert(printFloat(1.0L, f) == "0x1.000p+0");
318 assert(printFloat(3.3L, f) == "0x1.a66p+1");
319 assert(printFloat(2.9L, f) == "0x1.733p+1");
323 @safe unittest
325 auto f = FormatSpec!dchar("");
326 f.spec = 'a';
327 f.precision = 0;
329 assert(printFloat(1.0f, f) == "0x1p+0");
330 assert(printFloat(3.3f, f) == "0x2p+1");
331 assert(printFloat(2.9f, f) == "0x1p+1");
333 assert(printFloat(1.0, f) == "0x1p+0");
334 assert(printFloat(3.3, f) == "0x2p+1");
335 assert(printFloat(2.9, f) == "0x1p+1");
337 static if (real.mant_dig == 64)
339 assert(printFloat(1.0L, f) == "0x1p+0");
340 assert(printFloat(3.3L, f) == "0x2p+1");
341 assert(printFloat(2.9L, f) == "0x1p+1");
345 @safe unittest
347 auto f = FormatSpec!dchar("");
348 f.spec = 'a';
349 f.precision = 0;
350 f.flHash = true;
352 assert(printFloat(1.0f, f) == "0x1.p+0");
353 assert(printFloat(3.3f, f) == "0x2.p+1");
354 assert(printFloat(2.9f, f) == "0x1.p+1");
356 assert(printFloat(1.0, f) == "0x1.p+0");
357 assert(printFloat(3.3, f) == "0x2.p+1");
358 assert(printFloat(2.9, f) == "0x1.p+1");
360 static if (real.mant_dig == 64)
362 assert(printFloat(1.0L, f) == "0x1.p+0");
363 assert(printFloat(3.3L, f) == "0x2.p+1");
364 assert(printFloat(2.9L, f) == "0x1.p+1");
368 @safe unittest
370 auto f = FormatSpec!dchar("");
371 f.spec = 'a';
372 f.width = 22;
374 assert(printFloat(1.0f, f) == " 0x1p+0");
375 assert(printFloat(3.3f, f) == " 0x1.a66666p+1");
376 assert(printFloat(2.9f, f) == " 0x1.733334p+1");
378 assert(printFloat(1.0, f) == " 0x1p+0");
379 assert(printFloat(3.3, f) == " 0x1.a666666666666p+1");
380 assert(printFloat(2.9, f) == " 0x1.7333333333333p+1");
382 static if (real.mant_dig == 64)
384 f.width = 25;
385 assert(printFloat(1.0L, f) == " 0x1p+0");
386 assert(printFloat(3.3L, f) == " 0x1.a666666666666666p+1");
387 assert(printFloat(2.9L, f) == " 0x1.7333333333333334p+1");
391 @safe unittest
393 auto f = FormatSpec!dchar("");
394 f.spec = 'a';
395 f.width = 22;
396 f.flDash = true;
398 assert(printFloat(1.0f, f) == "0x1p+0 ");
399 assert(printFloat(3.3f, f) == "0x1.a66666p+1 ");
400 assert(printFloat(2.9f, f) == "0x1.733334p+1 ");
402 assert(printFloat(1.0, f) == "0x1p+0 ");
403 assert(printFloat(3.3, f) == "0x1.a666666666666p+1 ");
404 assert(printFloat(2.9, f) == "0x1.7333333333333p+1 ");
406 static if (real.mant_dig == 64)
408 f.width = 25;
409 assert(printFloat(1.0L, f) == "0x1p+0 ");
410 assert(printFloat(3.3L, f) == "0x1.a666666666666666p+1 ");
411 assert(printFloat(2.9L, f) == "0x1.7333333333333334p+1 ");
415 @safe unittest
417 auto f = FormatSpec!dchar("");
418 f.spec = 'a';
419 f.width = 22;
420 f.flZero = true;
422 assert(printFloat(1.0f, f) == "0x00000000000000001p+0");
423 assert(printFloat(3.3f, f) == "0x0000000001.a66666p+1");
424 assert(printFloat(2.9f, f) == "0x0000000001.733334p+1");
426 assert(printFloat(1.0, f) == "0x00000000000000001p+0");
427 assert(printFloat(3.3, f) == "0x001.a666666666666p+1");
428 assert(printFloat(2.9, f) == "0x001.7333333333333p+1");
430 static if (real.mant_dig == 64)
432 f.width = 25;
433 assert(printFloat(1.0L, f) == "0x00000000000000000001p+0");
434 assert(printFloat(3.3L, f) == "0x001.a666666666666666p+1");
435 assert(printFloat(2.9L, f) == "0x001.7333333333333334p+1");
439 @safe unittest
441 auto f = FormatSpec!dchar("");
442 f.spec = 'a';
443 f.width = 22;
444 f.flPlus = true;
446 assert(printFloat(1.0f, f) == " +0x1p+0");
447 assert(printFloat(3.3f, f) == " +0x1.a66666p+1");
448 assert(printFloat(2.9f, f) == " +0x1.733334p+1");
450 assert(printFloat(1.0, f) == " +0x1p+0");
451 assert(printFloat(3.3, f) == " +0x1.a666666666666p+1");
452 assert(printFloat(2.9, f) == " +0x1.7333333333333p+1");
454 static if (real.mant_dig == 64)
456 f.width = 25;
457 assert(printFloat(1.0L, f) == " +0x1p+0");
458 assert(printFloat(3.3L, f) == " +0x1.a666666666666666p+1");
459 assert(printFloat(2.9L, f) == " +0x1.7333333333333334p+1");
463 @safe unittest
465 auto f = FormatSpec!dchar("");
466 f.spec = 'a';
467 f.width = 22;
468 f.flDash = true;
469 f.flSpace = true;
471 assert(printFloat(1.0f, f) == " 0x1p+0 ");
472 assert(printFloat(3.3f, f) == " 0x1.a66666p+1 ");
473 assert(printFloat(2.9f, f) == " 0x1.733334p+1 ");
475 assert(printFloat(1.0, f) == " 0x1p+0 ");
476 assert(printFloat(3.3, f) == " 0x1.a666666666666p+1 ");
477 assert(printFloat(2.9, f) == " 0x1.7333333333333p+1 ");
479 static if (real.mant_dig == 64)
481 f.width = 25;
482 assert(printFloat(1.0L, f) == " 0x1p+0 ");
483 assert(printFloat(3.3L, f) == " 0x1.a666666666666666p+1 ");
484 assert(printFloat(2.9L, f) == " 0x1.7333333333333334p+1 ");
488 @safe unittest
490 import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
492 // std.math's FloatingPointControl isn't available on all target platforms
493 static if (is(FloatingPointControl))
495 FloatingPointControl fpctrl;
497 auto f = FormatSpec!dchar("");
498 f.spec = 'a';
499 f.precision = 1;
501 fpctrl.rounding = FloatingPointControl.roundToNearest;
503 /* tiesAwayFromZero currently not supported
504 assert(printFloat(0x1.18p0, f) == "0x1.2p+0");
505 assert(printFloat(0x1.28p0, f) == "0x1.3p+0");
506 assert(printFloat(0x1.1ap0, f) == "0x1.2p+0");
507 assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
508 assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
509 assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0");
510 assert(printFloat(-0x1.28p0, f) == "-0x1.3p+0");
511 assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0");
512 assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
513 assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
516 assert(printFloat(0x1.18p0, f) == "0x1.2p+0");
517 assert(printFloat(0x1.28p0, f) == "0x1.2p+0");
518 assert(printFloat(0x1.1ap0, f) == "0x1.2p+0");
519 assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
520 assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
521 assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0");
522 assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0");
523 assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0");
524 assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
525 assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
527 fpctrl.rounding = FloatingPointControl.roundToZero;
529 assert(printFloat(0x1.18p0, f) == "0x1.1p+0");
530 assert(printFloat(0x1.28p0, f) == "0x1.2p+0");
531 assert(printFloat(0x1.1ap0, f) == "0x1.1p+0");
532 assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
533 assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
534 assert(printFloat(-0x1.18p0, f) == "-0x1.1p+0");
535 assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0");
536 assert(printFloat(-0x1.1ap0, f) == "-0x1.1p+0");
537 assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
538 assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
540 fpctrl.rounding = FloatingPointControl.roundUp;
542 assert(printFloat(0x1.18p0, f) == "0x1.2p+0");
543 assert(printFloat(0x1.28p0, f) == "0x1.3p+0");
544 assert(printFloat(0x1.1ap0, f) == "0x1.2p+0");
545 assert(printFloat(0x1.16p0, f) == "0x1.2p+0");
546 assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
547 assert(printFloat(-0x1.18p0, f) == "-0x1.1p+0");
548 assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0");
549 assert(printFloat(-0x1.1ap0, f) == "-0x1.1p+0");
550 assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
551 assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
553 fpctrl.rounding = FloatingPointControl.roundDown;
555 assert(printFloat(0x1.18p0, f) == "0x1.1p+0");
556 assert(printFloat(0x1.28p0, f) == "0x1.2p+0");
557 assert(printFloat(0x1.1ap0, f) == "0x1.1p+0");
558 assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
559 assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
560 assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0");
561 assert(printFloat(-0x1.28p0, f) == "-0x1.3p+0");
562 assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0");
563 assert(printFloat(-0x1.16p0, f) == "-0x1.2p+0");
564 assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
568 // for 100% coverage
569 @safe unittest
571 auto f = FormatSpec!dchar("");
572 f.spec = 'a';
573 f.precision = 3;
575 assert(printFloat(0x1.19f81p0, f) == "0x1.1a0p+0");
576 assert(printFloat(0x1.19f01p0, f) == "0x1.19fp+0");
579 @safe unittest
581 auto f = FormatSpec!dchar("");
582 f.spec = 'A';
583 f.precision = 3;
585 assert(printFloat(0x1.19f81p0, f) == "0X1.1A0P+0");
586 assert(printFloat(0x1.19f01p0, f) == "0X1.19FP+0");
589 private void printFloatE(bool g, Writer, T, Char)(auto ref Writer w, const(T) val,
590 FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
591 if (is(T == float) || is(T == double)
592 || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
594 import std.format.internal.write : writeAligned, PrecisionType, RoundingClass, round;
596 static if (!g)
598 if (f.precision == f.UNSPECIFIED)
599 f.precision = 6;
602 // special treatment for 0.0
603 if (mnt == 0)
605 static if (g)
606 writeAligned(w, sgn, "0", ".", "", f, PrecisionType.allDigits);
607 else
608 writeAligned(w, sgn, "0", ".", is_upper ? "E+00" : "e+00", f, PrecisionType.fractionalDigits);
609 return;
612 char[T.mant_dig + T.max_exp] dec_buf;
613 char[T.max_10_exp.stringof.length + 2] exp_buf;
615 int final_exp = 0;
617 RoundingClass rc;
619 // Depending on exp, we will use one of three algorithms:
621 // Algorithm A: For large exponents (exp >= T.mant_dig)
622 // Algorithm B: For small exponents (exp < T.mant_dig - 61)
623 // Algorithm C: For exponents close to 0.
625 // Algorithm A:
626 // The number to print looks like this: mantissa followed by several zeros.
628 // We know, that there is no fractional part, so we can just use integer division,
629 // consecutivly dividing by 10 and writing down the remainder from right to left.
630 // Unfortunately the integer is too large to fit in an ulong, so we use something
631 // like BigInt: An array of ulongs. We only use 60 bits of that ulongs, because
632 // this simplifies (and speeds up) the division to come.
634 // For the division we use integer division with reminder for each ulong and put
635 // the reminder of each step in the first 4 bits of ulong of the next step (think of
636 // long division for the rationale behind this). The final reminder is the next
637 // digit (from right to left).
639 // This results in the output we would have for the %f specifier. We now adjust this
640 // for %e: First we calculate the place, where the exponent should be printed, filling
641 // up with zeros if needed and second we move the leftmost digit one to the left
642 // and inserting a dot.
644 // After that we decide on the rounding type, using the digits right of the position,
645 // where the exponent will be printed (currently they are still there, but will be
646 // overwritten later).
648 // Algorithm B:
649 // The number to print looks like this: zero dot several zeros followed by the mantissa
651 // We know, that the number has no integer part. The algorithm consecutivly multiplies
652 // by 10. The integer part (rounded down) after the multiplication is the next digit
653 // (from left to right). This integer part is removed after each step.
654 // Again, the number is represented as an array of ulongs, with only 60 bits used of
655 // every ulong.
657 // For the multiplication we use normal integer multiplication, which can result in digits
658 // in the uppermost 4 bits. These 4 digits are the carry which is added to the result
659 // of the next multiplication and finally the last carry is the next digit.
661 // Other than for the %f specifier, this multiplication is splitted into two almost
662 // identical parts. The first part lasts as long as we find zeros. We need to do this
663 // to calculate the correct exponent.
665 // The second part will stop, when only zeros remain or when we've got enough digits
666 // for the requested precision. In the second case, we have to find out, which rounding
667 // we have. Aside from special cases we do this by calculating one more digit.
669 // Algorithm C:
670 // This time, we know, that the integral part and the fractional part each fit into a
671 // ulong. The mantissa might be partially in both parts or completely in the fractional
672 // part.
674 // We first calculate the integral part by consecutive division by 10. Depending on the
675 // precision this might result in more digits, than we need. In that case we calculate
676 // the position of the exponent and the rounding type.
678 // If there is no integral part, we need to find the first non zero digit. We do this by
679 // consecutive multiplication by 10, saving the first non zero digit followed by a dot.
681 // In either case, we continue filling up with the fractional part until we have enough
682 // digits. If still necessary, we decide the rounding type, mainly by looking at the
683 // next digit.
685 size_t right = 1;
686 size_t start = 1;
687 size_t left = 1;
689 static if (is(T == real) && real.mant_dig == 64)
691 enum small_bound = 0;
692 enum max_buf = 275;
694 else
696 enum small_bound = T.mant_dig - 61;
697 static if (is(T == float))
698 enum max_buf = 4;
699 else
700 enum max_buf = 18;
703 ulong[max_buf] bigbuf;
704 if (exp >= T.mant_dig)
706 start = left = right = dec_buf.length;
708 // large number without fractional digits
710 // As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits,
711 // because this makes it much more easy to implement the division by 10.
712 int count = exp / 60 + 1;
714 // only the first few ulongs contain the mantiassa. The rest are zeros.
715 int lower = 60 - (exp - T.mant_dig + 1) % 60;
717 static if (is(T == real) && real.mant_dig == 64)
719 // for x87 reals, the lowest ulong may contain more than 60 bits,
720 // because the mantissa is 63 (>60) bits long
721 // therefore we need one ulong less
722 if (lower <= 3) count--;
725 // saved in big endian format
726 ulong[] mybig = bigbuf[0 .. count];
728 if (lower < T.mant_dig)
730 mybig[0] = mnt >> lower;
731 mybig[1] = (mnt & ((1L << lower) - 1)) << 60 - lower;
733 else
734 mybig[0] = (mnt & ((1L << lower) - 1)) << 60 - lower;
736 // Generation of digits by consecutive division with reminder by 10.
737 int msu = 0; // Most significant ulong; when it get's zero, we can ignore it further on
738 while (msu < count - 1 || mybig[$ - 1] != 0)
740 ulong mod = 0;
741 foreach (i;msu .. count)
743 mybig[i] |= mod << 60;
744 mod = mybig[i] % 10;
745 mybig[i] /= 10;
747 if (mybig[msu] == 0)
748 ++msu;
750 dec_buf[--left] = cast(byte) ('0' + mod);
751 ++final_exp;
753 --final_exp;
755 static if (g)
756 start = left + f.precision;
757 else
758 start = left + f.precision + 1;
760 // move leftmost digit one more left and add dot between
761 dec_buf[left - 1] = dec_buf[left];
762 dec_buf[left] = '.';
763 --left;
765 // rounding type
766 if (start >= right)
767 rc = RoundingClass.ZERO;
768 else if (dec_buf[start] != '0' && dec_buf[start] != '5')
769 rc = dec_buf[start] > '5' ? RoundingClass.UPPER : RoundingClass.LOWER;
770 else
772 rc = dec_buf[start] == '5' ? RoundingClass.FIVE : RoundingClass.ZERO;
773 foreach (i; start + 1 .. right)
774 if (dec_buf[i] > '0')
776 rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER;
777 break;
781 if (start < right) right = start;
783 else if (exp < small_bound)
785 // small number without integer digits
787 // Again this number does not fit in a ulong and we use an array of ulongs. And again we
788 // only use 60 bits, because this simplifies the multiplication by 10.
789 int count = (T.mant_dig - exp - 2) / 60 + 1;
791 // saved in little endian format
792 ulong[] mybig = bigbuf[0 .. count];
794 // only the last few ulongs contain the mantiassa. Because of little endian
795 // format these are the ulongs at index 0 and 1 (and 2 in case of x87 reals).
796 // The rest are zeros.
797 int upper = 60 - (-exp - 1) % 60;
799 static if (is(T == real) && real.mant_dig == 64)
801 if (upper < 4)
803 mybig[0] = (mnt & ((1L << (4 - upper)) - 1)) << 56 + upper;
804 mybig[1] = (mnt >> (4 - upper)) & ((1L << 60) - 1);
805 mybig[2] = mnt >> 64 - upper;
807 else
809 mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
810 mybig[1] = mnt >> (T.mant_dig - upper);
813 else
815 if (upper < T.mant_dig)
817 mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
818 mybig[1] = mnt >> (T.mant_dig - upper);
820 else
821 mybig[0] = mnt << (upper - T.mant_dig);
824 int lsu = 0; // Least significant ulong; when it get's zero, we can ignore it further on
826 // adding zeros, until we reach first nonzero
827 while (lsu < count - 1 || mybig[$ - 1]!=0)
829 ulong over = 0;
830 foreach (i; lsu .. count)
832 mybig[i] = mybig[i] * 10 + over;
833 over = mybig[i] >> 60;
834 mybig[i] &= (1L << 60) - 1;
836 if (mybig[lsu] == 0)
837 ++lsu;
838 --final_exp;
840 if (over != 0)
842 dec_buf[right++] = cast(byte) ('0' + over);
843 dec_buf[right++] = '.';
844 break;
848 // adding more digits
849 static if (g)
850 start = right - 1;
851 else
852 start = right;
853 while ((lsu < count - 1 || mybig[$ - 1] != 0) && right - start < f.precision)
855 ulong over = 0;
856 foreach (i;lsu .. count)
858 mybig[i] = mybig[i] * 10 + over;
859 over = mybig[i] >> 60;
860 mybig[i] &= (1L << 60) - 1;
862 if (mybig[lsu] == 0)
863 ++lsu;
865 dec_buf[right++] = cast(byte) ('0' + over);
868 // rounding type
869 if (lsu >= count - 1 && mybig[count - 1] == 0)
870 rc = RoundingClass.ZERO;
871 else if (lsu == count - 1 && mybig[lsu] == 1L << 59)
872 rc = RoundingClass.FIVE;
873 else
875 ulong over = 0;
876 foreach (i;lsu .. count)
878 mybig[i] = mybig[i] * 10 + over;
879 over = mybig[i] >> 60;
880 mybig[i] &= (1L << 60) - 1;
882 rc = over >= 5 ? RoundingClass.UPPER : RoundingClass.LOWER;
885 else
887 // medium sized number, probably with integer and fractional digits
888 // this is fastest, because both parts fit into a ulong each
889 ulong int_part = mnt >> (T.mant_dig - 1 - exp);
890 ulong frac_part = mnt & ((1L << (T.mant_dig - 1 - exp)) - 1);
892 // for x87 reals the mantiassa might be up to 3 bits too long
893 // we need to save these bits as a tail and handle this separately
894 static if (is(T == real) && real.mant_dig == 64)
896 ulong tail = 0;
897 ulong tail_length = 0;
898 if (exp < 3)
900 tail = frac_part & ((1L << (3 - exp)) - 1);
901 tail_length = 3 - exp;
902 frac_part >>= 3 - exp;
903 exp = 3;
907 start = 0;
909 // could we already decide on the rounding mode in the integer part?
910 bool found = false;
912 if (int_part > 0)
914 import core.bitop : bsr;
915 left = right = int_part.bsr * 100 / 332 + 4;
917 // integer part, if there is something to print
918 while (int_part >= 10)
920 dec_buf[--left] = '0' + (int_part % 10);
921 int_part /= 10;
922 ++final_exp;
923 ++start;
926 dec_buf[--left] = '.';
927 dec_buf[--left] = cast(byte) ('0' + int_part);
929 static if (g)
930 auto limit = f.precision + 1;
931 else
932 auto limit = f.precision + 2;
934 if (right - left > limit)
936 auto old_right = right;
937 right = left + limit;
939 if (dec_buf[right] == '5' || dec_buf[right] == '0')
941 rc = dec_buf[right] == '5' ? RoundingClass.FIVE : RoundingClass.ZERO;
942 if (frac_part != 0)
943 rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER;
944 else
945 foreach (i;right + 1 .. old_right)
946 if (dec_buf[i] > '0')
948 rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER;
949 break;
952 else
953 rc = dec_buf[right] > '5' ? RoundingClass.UPPER : RoundingClass.LOWER;
954 found = true;
957 else
959 // fractional part, skipping leading zeros
960 while (frac_part != 0)
962 --final_exp;
963 frac_part *= 10;
964 static if (is(T == real) && real.mant_dig == 64)
966 if (tail_length > 0)
968 // together this is *= 10;
969 tail *= 5;
970 tail_length--;
972 frac_part += tail >> tail_length;
973 if (tail_length > 0)
974 tail &= (1L << tail_length) - 1;
977 auto tmp = frac_part >> (T.mant_dig - 1 - exp);
978 frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
979 if (tmp > 0)
981 dec_buf[right++] = cast(byte) ('0' + tmp);
982 dec_buf[right++] = '.';
983 break;
987 rc = RoundingClass.ZERO;
990 static if (g)
991 size_t limit = f.precision - 1;
992 else
993 size_t limit = f.precision;
995 // the fractional part after the zeros
996 while (frac_part != 0 && start < limit)
998 frac_part *= 10;
999 static if (is(T == real) && real.mant_dig == 64)
1001 if (tail_length > 0)
1003 // together this is *= 10;
1004 tail *= 5;
1005 tail_length--;
1007 frac_part += tail >> tail_length;
1008 if (tail_length > 0)
1009 tail &= (1L << tail_length) - 1;
1012 dec_buf[right++] = cast(byte) ('0' + (frac_part >> (T.mant_dig - 1 - exp)));
1013 frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
1014 ++start;
1017 static if (g)
1018 limit = right - left - 1;
1019 else
1020 limit = start;
1022 // rounding mode, if not allready known
1023 if (frac_part != 0 && !found)
1025 frac_part *= 10;
1026 auto nextDigit = frac_part >> (T.mant_dig - 1 - exp);
1027 frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
1029 if (nextDigit == 5 && frac_part == 0)
1030 rc = RoundingClass.FIVE;
1031 else if (nextDigit >= 5)
1032 rc = RoundingClass.UPPER;
1033 else
1034 rc = RoundingClass.LOWER;
1038 if (round(dec_buf, left, right, rc, sgn == "-"))
1040 left--;
1041 right--;
1042 dec_buf[left + 2] = dec_buf[left + 1];
1043 dec_buf[left + 1] = '.';
1044 final_exp++;
1047 // printing exponent
1048 auto neg = final_exp < 0;
1049 if (neg) final_exp = -final_exp;
1051 size_t exp_pos = exp_buf.length;
1055 exp_buf[--exp_pos] = '0' + final_exp%10;
1056 final_exp /= 10;
1057 } while (final_exp > 0);
1058 if (exp_buf.length - exp_pos == 1)
1059 exp_buf[--exp_pos] = '0';
1060 exp_buf[--exp_pos] = neg ? '-' : '+';
1061 exp_buf[--exp_pos] = is_upper ? 'E' : 'e';
1063 while (right > left + 1 && dec_buf[right - 1] == '0') right--;
1065 if (right == left + 1)
1066 dec_buf[right++] = '.';
1068 static if (g)
1069 writeAligned(w, sgn, dec_buf[left .. left + 1], dec_buf[left + 1 .. right],
1070 exp_buf[exp_pos .. $], f, PrecisionType.allDigits);
1071 else
1072 writeAligned(w, sgn, dec_buf[left .. left + 1], dec_buf[left + 1 .. right],
1073 exp_buf[exp_pos .. $], f, PrecisionType.fractionalDigits);
1076 @safe unittest
1078 auto f = FormatSpec!dchar("");
1079 f.spec = 'e';
1080 assert(printFloat(float.nan, f) == "nan");
1081 assert(printFloat(-float.nan, f) == "-nan");
1082 assert(printFloat(float.infinity, f) == "inf");
1083 assert(printFloat(-float.infinity, f) == "-inf");
1084 assert(printFloat(0.0f, f) == "0.000000e+00");
1085 assert(printFloat(-0.0f, f) == "-0.000000e+00");
1086 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1087 assert(printFloat(cast(float) 1e-40, f) == "9.999946e-41");
1088 assert(printFloat(cast(float) -1e-40, f) == "-9.999946e-41");
1089 assert(printFloat(1e-30f, f) == "1.000000e-30");
1090 assert(printFloat(-1e-30f, f) == "-1.000000e-30");
1091 assert(printFloat(1e-10f, f) == "1.000000e-10");
1092 assert(printFloat(-1e-10f, f) == "-1.000000e-10");
1093 assert(printFloat(0.1f, f) == "1.000000e-01");
1094 assert(printFloat(-0.1f, f) == "-1.000000e-01");
1095 assert(printFloat(10.0f, f) == "1.000000e+01");
1096 assert(printFloat(-10.0f, f) == "-1.000000e+01");
1097 assert(printFloat(1e30f, f) == "1.000000e+30");
1098 assert(printFloat(-1e30f, f) == "-1.000000e+30");
1100 import std.math.operations : nextUp, nextDown;
1101 assert(printFloat(nextUp(0.0f), f) == "1.401298e-45");
1102 assert(printFloat(nextDown(-0.0f), f) == "-1.401298e-45");
1105 @safe unittest
1107 auto f = FormatSpec!dchar("");
1108 f.spec = 'e';
1109 f.width = 20;
1110 f.precision = 10;
1112 assert(printFloat(float.nan, f) == " nan");
1113 assert(printFloat(-float.nan, f) == " -nan");
1114 assert(printFloat(float.infinity, f) == " inf");
1115 assert(printFloat(-float.infinity, f) == " -inf");
1116 assert(printFloat(0.0f, f) == " 0.0000000000e+00");
1117 assert(printFloat(-0.0f, f) == " -0.0000000000e+00");
1118 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1119 assert(printFloat(cast(float) 1e-40, f) == " 9.9999461011e-41");
1120 assert(printFloat(cast(float) -1e-40, f) == " -9.9999461011e-41");
1121 assert(printFloat(1e-30f, f) == " 1.0000000032e-30");
1122 assert(printFloat(-1e-30f, f) == " -1.0000000032e-30");
1123 assert(printFloat(1e-10f, f) == " 1.0000000134e-10");
1124 assert(printFloat(-1e-10f, f) == " -1.0000000134e-10");
1125 assert(printFloat(0.1f, f) == " 1.0000000149e-01");
1126 assert(printFloat(-0.1f, f) == " -1.0000000149e-01");
1127 assert(printFloat(10.0f, f) == " 1.0000000000e+01");
1128 assert(printFloat(-10.0f, f) == " -1.0000000000e+01");
1129 assert(printFloat(1e30f, f) == " 1.0000000150e+30");
1130 assert(printFloat(-1e30f, f) == " -1.0000000150e+30");
1132 import std.math.operations : nextUp, nextDown;
1133 assert(printFloat(nextUp(0.0f), f) == " 1.4012984643e-45");
1134 assert(printFloat(nextDown(-0.0f), f) == " -1.4012984643e-45");
1137 @safe unittest
1139 auto f = FormatSpec!dchar("");
1140 f.spec = 'e';
1141 f.width = 20;
1142 f.precision = 10;
1143 f.flDash = true;
1145 assert(printFloat(float.nan, f) == "nan ");
1146 assert(printFloat(-float.nan, f) == "-nan ");
1147 assert(printFloat(float.infinity, f) == "inf ");
1148 assert(printFloat(-float.infinity, f) == "-inf ");
1149 assert(printFloat(0.0f, f) == "0.0000000000e+00 ");
1150 assert(printFloat(-0.0f, f) == "-0.0000000000e+00 ");
1151 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1152 assert(printFloat(cast(float) 1e-40, f) == "9.9999461011e-41 ");
1153 assert(printFloat(cast(float) -1e-40, f) == "-9.9999461011e-41 ");
1154 assert(printFloat(1e-30f, f) == "1.0000000032e-30 ");
1155 assert(printFloat(-1e-30f, f) == "-1.0000000032e-30 ");
1156 assert(printFloat(1e-10f, f) == "1.0000000134e-10 ");
1157 assert(printFloat(-1e-10f, f) == "-1.0000000134e-10 ");
1158 assert(printFloat(0.1f, f) == "1.0000000149e-01 ");
1159 assert(printFloat(-0.1f, f) == "-1.0000000149e-01 ");
1160 assert(printFloat(10.0f, f) == "1.0000000000e+01 ");
1161 assert(printFloat(-10.0f, f) == "-1.0000000000e+01 ");
1162 assert(printFloat(1e30f, f) == "1.0000000150e+30 ");
1163 assert(printFloat(-1e30f, f) == "-1.0000000150e+30 ");
1165 import std.math.operations : nextUp, nextDown;
1166 assert(printFloat(nextUp(0.0f), f) == "1.4012984643e-45 ");
1167 assert(printFloat(nextDown(-0.0f), f) == "-1.4012984643e-45 ");
1170 @safe unittest
1172 auto f = FormatSpec!dchar("");
1173 f.spec = 'e';
1174 f.width = 20;
1175 f.precision = 10;
1176 f.flZero = true;
1178 assert(printFloat(float.nan, f) == " nan");
1179 assert(printFloat(-float.nan, f) == " -nan");
1180 assert(printFloat(float.infinity, f) == " inf");
1181 assert(printFloat(-float.infinity, f) == " -inf");
1182 assert(printFloat(0.0f, f) == "00000.0000000000e+00");
1183 assert(printFloat(-0.0f, f) == "-0000.0000000000e+00");
1184 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1185 assert(printFloat(cast(float) 1e-40, f) == "00009.9999461011e-41");
1186 assert(printFloat(cast(float) -1e-40, f) == "-0009.9999461011e-41");
1187 assert(printFloat(1e-30f, f) == "00001.0000000032e-30");
1188 assert(printFloat(-1e-30f, f) == "-0001.0000000032e-30");
1189 assert(printFloat(1e-10f, f) == "00001.0000000134e-10");
1190 assert(printFloat(-1e-10f, f) == "-0001.0000000134e-10");
1191 assert(printFloat(0.1f, f) == "00001.0000000149e-01");
1192 assert(printFloat(-0.1f, f) == "-0001.0000000149e-01");
1193 assert(printFloat(10.0f, f) == "00001.0000000000e+01");
1194 assert(printFloat(-10.0f, f) == "-0001.0000000000e+01");
1195 assert(printFloat(1e30f, f) == "00001.0000000150e+30");
1196 assert(printFloat(-1e30f, f) == "-0001.0000000150e+30");
1198 import std.math.operations : nextUp, nextDown;
1199 assert(printFloat(nextUp(0.0f), f) == "00001.4012984643e-45");
1200 assert(printFloat(nextDown(-0.0f), f) == "-0001.4012984643e-45");
1203 @safe unittest
1205 import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
1207 // std.math's FloatingPointControl isn't available on all target platforms
1208 static if (is(FloatingPointControl))
1210 FloatingPointControl fpctrl;
1212 auto f = FormatSpec!dchar("");
1213 f.spec = 'e';
1214 f.precision = 1;
1216 fpctrl.rounding = FloatingPointControl.roundToNearest;
1219 assert(printFloat(11.5f, f) == "1.2e+01");
1220 assert(printFloat(12.5f, f) == "1.3e+01");
1221 assert(printFloat(11.7f, f) == "1.2e+01");
1222 assert(printFloat(11.3f, f) == "1.1e+01");
1223 assert(printFloat(11.0f, f) == "1.1e+01");
1224 assert(printFloat(-11.5f, f) == "-1.2e+01");
1225 assert(printFloat(-12.5f, f) == "-1.3e+01");
1226 assert(printFloat(-11.7f, f) == "-1.2e+01");
1227 assert(printFloat(-11.3f, f) == "-1.1e+01");
1228 assert(printFloat(-11.0f, f) == "-1.1e+01");
1231 assert(printFloat(11.5f, f) == "1.2e+01");
1232 assert(printFloat(12.5f, f) == "1.2e+01");
1233 assert(printFloat(11.7f, f) == "1.2e+01");
1234 assert(printFloat(11.3f, f) == "1.1e+01");
1235 assert(printFloat(11.0f, f) == "1.1e+01");
1236 assert(printFloat(-11.5f, f) == "-1.2e+01");
1237 assert(printFloat(-12.5f, f) == "-1.2e+01");
1238 assert(printFloat(-11.7f, f) == "-1.2e+01");
1239 assert(printFloat(-11.3f, f) == "-1.1e+01");
1240 assert(printFloat(-11.0f, f) == "-1.1e+01");
1242 fpctrl.rounding = FloatingPointControl.roundToZero;
1244 assert(printFloat(11.5f, f) == "1.1e+01");
1245 assert(printFloat(12.5f, f) == "1.2e+01");
1246 assert(printFloat(11.7f, f) == "1.1e+01");
1247 assert(printFloat(11.3f, f) == "1.1e+01");
1248 assert(printFloat(11.0f, f) == "1.1e+01");
1249 assert(printFloat(-11.5f, f) == "-1.1e+01");
1250 assert(printFloat(-12.5f, f) == "-1.2e+01");
1251 assert(printFloat(-11.7f, f) == "-1.1e+01");
1252 assert(printFloat(-11.3f, f) == "-1.1e+01");
1253 assert(printFloat(-11.0f, f) == "-1.1e+01");
1255 fpctrl.rounding = FloatingPointControl.roundUp;
1257 assert(printFloat(11.5f, f) == "1.2e+01");
1258 assert(printFloat(12.5f, f) == "1.3e+01");
1259 assert(printFloat(11.7f, f) == "1.2e+01");
1260 assert(printFloat(11.3f, f) == "1.2e+01");
1261 assert(printFloat(11.0f, f) == "1.1e+01");
1262 assert(printFloat(-11.5f, f) == "-1.1e+01");
1263 assert(printFloat(-12.5f, f) == "-1.2e+01");
1264 assert(printFloat(-11.7f, f) == "-1.1e+01");
1265 assert(printFloat(-11.3f, f) == "-1.1e+01");
1266 assert(printFloat(-11.0f, f) == "-1.1e+01");
1268 fpctrl.rounding = FloatingPointControl.roundDown;
1270 assert(printFloat(11.5f, f) == "1.1e+01");
1271 assert(printFloat(12.5f, f) == "1.2e+01");
1272 assert(printFloat(11.7f, f) == "1.1e+01");
1273 assert(printFloat(11.3f, f) == "1.1e+01");
1274 assert(printFloat(11.0f, f) == "1.1e+01");
1275 assert(printFloat(-11.5f, f) == "-1.2e+01");
1276 assert(printFloat(-12.5f, f) == "-1.3e+01");
1277 assert(printFloat(-11.7f, f) == "-1.2e+01");
1278 assert(printFloat(-11.3f, f) == "-1.2e+01");
1279 assert(printFloat(-11.0f, f) == "-1.1e+01");
1283 @safe unittest
1285 auto f = FormatSpec!dchar("");
1286 f.spec = 'e';
1287 assert(printFloat(double.nan, f) == "nan");
1288 assert(printFloat(-double.nan, f) == "-nan");
1289 assert(printFloat(double.infinity, f) == "inf");
1290 assert(printFloat(-double.infinity, f) == "-inf");
1291 assert(printFloat(0.0, f) == "0.000000e+00");
1292 assert(printFloat(-0.0, f) == "-0.000000e+00");
1293 // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1294 assert(printFloat(1e-307 / 1000, f) == "1.000000e-310");
1295 assert(printFloat(-1e-307 / 1000, f) == "-1.000000e-310");
1296 assert(printFloat(1e-30, f) == "1.000000e-30");
1297 assert(printFloat(-1e-30, f) == "-1.000000e-30");
1298 assert(printFloat(1e-10, f) == "1.000000e-10");
1299 assert(printFloat(-1e-10, f) == "-1.000000e-10");
1300 assert(printFloat(0.1, f) == "1.000000e-01");
1301 assert(printFloat(-0.1, f) == "-1.000000e-01");
1302 assert(printFloat(10.0, f) == "1.000000e+01");
1303 assert(printFloat(-10.0, f) == "-1.000000e+01");
1304 assert(printFloat(1e300, f) == "1.000000e+300");
1305 assert(printFloat(-1e300, f) == "-1.000000e+300");
1307 import std.math.operations : nextUp, nextDown;
1308 assert(printFloat(nextUp(0.0), f) == "4.940656e-324");
1309 assert(printFloat(nextDown(-0.0), f) == "-4.940656e-324");
1312 @safe unittest
1314 static if (real.mant_dig > 64)
1316 pragma(msg, "printFloat tests disabled because of unsupported `real` format");
1318 else
1320 auto f = FormatSpec!dchar("");
1321 f.spec = 'e';
1322 assert(printFloat(real.nan, f) == "nan");
1323 assert(printFloat(-real.nan, f) == "-nan");
1324 assert(printFloat(real.infinity, f) == "inf");
1325 assert(printFloat(-real.infinity, f) == "-inf");
1329 @safe unittest
1331 auto f = FormatSpec!dchar("");
1332 f.spec = 'e';
1334 import std.math.operations : nextUp;
1336 double eps = nextUp(0.0);
1337 f.precision = 1000;
1338 assert(printFloat(eps, f) ==
1339 "4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599"
1340 ~"23797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036"
1341 ~"88718636056998730723050006387409153564984387312473397273169615140031715385398074126238565591171026"
1342 ~"65855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332"
1343 ~"45247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431"
1344 ~"93609238289345836806010601150616980975307834227731832924790498252473077637592724787465608477820373"
1345 ~"44696995336470179726777175851256605511991315048911014510378627381672509558373897335989936648099411"
1346 ~"64205702637090279242767544565229087538682506419718265533447265625000000000000000000000000000000000"
1347 ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1348 ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1349 ~"000000000000000000000e-324");
1351 f.precision = 50;
1352 assert(printFloat(double.max, f) ==
1353 "1.79769313486231570814527423731704356798070567525845e+308");
1354 assert(printFloat(double.epsilon, f) ==
1355 "2.22044604925031308084726333618164062500000000000000e-16");
1357 f.precision = 10;
1358 assert(printFloat(1.0/3.0, f) == "3.3333333333e-01");
1359 assert(printFloat(1.0/7.0, f) == "1.4285714286e-01");
1360 assert(printFloat(1.0/9.0, f) == "1.1111111111e-01");
1363 @safe unittest
1365 auto f = FormatSpec!dchar("");
1366 f.spec = 'e';
1367 f.precision = 15;
1369 import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
1370 LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
1372 assert(printFloat(cast(double) E, f) == "2.718281828459045e+00");
1373 assert(printFloat(cast(double) PI, f) == "3.141592653589793e+00");
1374 assert(printFloat(cast(double) PI_2, f) == "1.570796326794897e+00");
1375 assert(printFloat(cast(double) PI_4, f) == "7.853981633974483e-01");
1376 assert(printFloat(cast(double) M_1_PI, f) == "3.183098861837907e-01");
1377 assert(printFloat(cast(double) M_2_PI, f) == "6.366197723675814e-01");
1378 assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.128379167095513e+00");
1379 assert(printFloat(cast(double) LN10, f) == "2.302585092994046e+00");
1380 assert(printFloat(cast(double) LN2, f) == "6.931471805599453e-01");
1381 assert(printFloat(cast(double) LOG2, f) == "3.010299956639812e-01");
1382 assert(printFloat(cast(double) LOG2E, f) == "1.442695040888963e+00");
1383 assert(printFloat(cast(double) LOG2T, f) == "3.321928094887362e+00");
1384 assert(printFloat(cast(double) LOG10E, f) == "4.342944819032518e-01");
1385 assert(printFloat(cast(double) SQRT2, f) == "1.414213562373095e+00");
1386 assert(printFloat(cast(double) SQRT1_2, f) == "7.071067811865476e-01");
1389 // for 100% coverage
1390 @safe unittest
1392 import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
1394 auto f = FormatSpec!dchar("");
1395 f.spec = 'E';
1396 f.precision = 80;
1397 assert(printFloat(5.62776e+12f, f) ==
1398 "5.62775982080000000000000000000000000000000000000000000000000000000000000000000000E+12");
1400 f.precision = 49;
1401 assert(printFloat(2.5997869e-12f, f) ==
1402 "2.5997869221999758693186777236405760049819946289062E-12");
1404 f.precision = 6;
1405 assert(printFloat(-1.1418613e+07f, f) == "-1.141861E+07");
1406 assert(printFloat(-1.368281e+07f, f) == "-1.368281E+07");
1408 f.precision = 1;
1409 assert(printFloat(-245.666f, f) == "-2.5E+02");
1411 static if (is(FloatingPointControl))
1413 FloatingPointControl fpctrl;
1415 fpctrl.rounding = FloatingPointControl.roundUp;
1417 f.precision = 0;
1418 assert(printFloat(709422.0f, f) == "8E+05");
1422 @safe unittest
1424 static if (real.mant_dig > 64)
1426 pragma(msg, "printFloat tests disabled because of unsupported `real` format");
1428 else
1430 auto f = FormatSpec!dchar("");
1431 f.spec = 'e';
1432 assert(printFloat(real.nan, f) == "nan");
1433 assert(printFloat(-real.nan, f) == "-nan");
1434 assert(printFloat(real.infinity, f) == "inf");
1435 assert(printFloat(-real.infinity, f) == "-inf");
1436 assert(printFloat(0.0L, f) == "0.000000e+00");
1437 assert(printFloat(-0.0L, f) == "-0.000000e+00");
1440 static if (real.mant_dig == 64)
1442 assert(printFloat(1e-4940L, f) == "1.000000e-4940");
1443 assert(printFloat(-1e-4940L, f) == "-1.000000e-4940");
1444 assert(printFloat(1e-30L, f) == "1.000000e-30");
1445 assert(printFloat(-1e-30L, f) == "-1.000000e-30");
1446 assert(printFloat(1e-10L, f) == "1.000000e-10");
1447 assert(printFloat(-1e-10L, f) == "-1.000000e-10");
1448 assert(printFloat(0.1L, f) == "1.000000e-01");
1449 assert(printFloat(-0.1L, f) == "-1.000000e-01");
1450 assert(printFloat(10.0L, f) == "1.000000e+01");
1451 assert(printFloat(-10.0L, f) == "-1.000000e+01");
1452 version (Windows) {} // issue 20972
1453 else
1455 assert(printFloat(1e4000L, f) == "1.000000e+4000");
1456 assert(printFloat(-1e4000L, f) == "-1.000000e+4000");
1459 import std.math.operations : nextUp, nextDown;
1460 assert(printFloat(nextUp(0.0L), f) == "3.645200e-4951");
1461 assert(printFloat(nextDown(-0.0L), f) == "-3.645200e-4951");
1465 @safe unittest
1467 import std.exception : assertCTFEable;
1468 import std.math.exponential : log2;
1469 import std.math.operations : nextDown;
1471 assertCTFEable!(
1473 // log2 is broken for x87-reals on some computers in CTFE
1474 // the following tests excludes these computers from the tests
1475 // (issue 21757)
1476 enum test = cast(int) log2(3.05e2312L);
1477 static if (real.mant_dig == 64 && test == 7681)
1479 auto f = FormatSpec!dchar("");
1480 f.spec = 'e';
1481 assert(printFloat(real.infinity, f) == "inf");
1482 assert(printFloat(10.0L, f) == "1.000000e+01");
1483 assert(printFloat(2.6080L, f) == "2.608000e+00");
1484 assert(printFloat(3.05e2312L, f) == "3.050000e+2312");
1486 f.precision = 60;
1487 assert(printFloat(2.65e-54L, f) ==
1488 "2.650000000000000000059009987400547013941028940935296547599415e-54");
1491 commented out, because CTFE is currently too slow for 5000 digits with extreme values
1493 f.precision = 5000;
1494 auto result2 = printFloat(1.2119e-4822L, f);
1495 assert(result2.length == 5008);
1496 assert(result2[$ - 20 .. $] == "60729486595339e-4822");
1497 auto result3 = printFloat(real.min_normal, f);
1498 assert(result3.length == 5008);
1499 assert(result3[$ - 20 .. $] == "20781410082267e-4932");
1500 auto result4 = printFloat(real.min_normal.nextDown, f);
1501 assert(result4.length == 5008);
1502 assert(result4[$ - 20 .. $] == "81413263331006e-4932");
1508 private void printFloatF(bool g, Writer, T, Char)(auto ref Writer w, const(T) val,
1509 FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
1510 if (is(T == float) || is(T == double)
1511 || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
1513 import std.format.internal.write : writeAligned, PrecisionType, RoundingClass, round;
1515 static if (!g)
1517 if (f.precision == f.UNSPECIFIED)
1518 f.precision = 6;
1521 // special treatment for 0.0
1522 if (exp == 0 && mnt == 0)
1524 writeAligned(w, sgn, "0", ".", "", f, PrecisionType.fractionalDigits);
1525 return;
1528 char[T.max_exp + T.mant_dig + 1] dec_buf;
1530 RoundingClass rc;
1532 // Depending on exp, we will use one of three algorithms:
1534 // Algorithm A: For large exponents (exp >= T.mant_dig)
1535 // Algorithm B: For small exponents (exp < T.mant_dig - 61)
1536 // Algorithm C: For exponents close to 0.
1538 // Algorithm A:
1539 // The number to print looks like this: mantissa followed by several zeros.
1541 // We know, that there is no fractional part, so we can just use integer division,
1542 // consecutivly dividing by 10 and writing down the remainder from right to left.
1543 // Unfortunately the integer is too large to fit in an ulong, so we use something
1544 // like BigInt: An array of ulongs. We only use 60 bits of that ulongs, because
1545 // this simplifies (and speeds up) the division to come.
1547 // For the division we use integer division with reminder for each ulong and put
1548 // the reminder of each step in the first 4 bits of ulong of the next step (think of
1549 // long division for the rationale behind this). The final reminder is the next
1550 // digit (from right to left).
1552 // Algorithm B:
1553 // The number to print looks like this: zero dot several zeros followed by the mantissa
1555 // We know, that the number has no integer part. The algorithm consecutivly multiplies
1556 // by 10. The integer part (rounded down) after the multiplication is the next digit
1557 // (from left to right). This integer part is removed after each step.
1558 // Again, the number is represented as an array of ulongs, with only 60 bits used of
1559 // every ulong.
1561 // For the multiplication we use normal integer multiplication, which can result in digits
1562 // in the uppermost 4 bits. These 4 digits are the carry which is added to the result
1563 // of the next multiplication and finally the last carry is the next digit.
1565 // The calculation will stop, when only zeros remain or when we've got enough digits
1566 // for the requested precision. In the second case, we have to find out, which rounding
1567 // we have. Aside from special cases we do this by calculating one more digit.
1569 // Algorithm C:
1570 // This time, we know, that the integral part and the fractional part each fit into a
1571 // ulong. The mantissa might be partially in both parts or completely in the fractional
1572 // part.
1574 // We first calculate the integral part by consecutive division by 10. Then we calculate
1575 // the fractional part by consecutive multiplication by 10. Again only until we have enough
1576 // digits. Finally, we decide the rounding type, mainly by looking at the next digit.
1578 static if (is(T == real) && real.mant_dig == 64)
1580 enum small_bound = 0;
1581 enum max_buf = 275;
1583 else
1585 enum small_bound = T.mant_dig - 61;
1586 static if (is(T == float))
1587 enum max_buf = 4;
1588 else
1589 enum max_buf = 18;
1592 size_t start = 2;
1593 size_t left = 2;
1594 size_t right = 2;
1596 ulong[max_buf] bigbuf;
1597 if (exp >= T.mant_dig)
1599 left = start = dec_buf.length - 1;
1600 right = dec_buf.length;
1601 dec_buf[start] = '.';
1603 // large number without fractional digits
1605 // As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits,
1606 // because this makes it much more easy to implement the division by 10.
1607 int count = exp / 60 + 1;
1609 // only the first few ulongs contain the mantiassa. The rest are zeros.
1610 int lower = 60 - (exp - T.mant_dig + 1) % 60;
1612 static if (is(T == real) && real.mant_dig == 64)
1614 // for x87 reals, the lowest ulong may contain more than 60 bits,
1615 // because the mantissa is 63 (>60) bits long
1616 // therefore we need one ulong less
1617 if (lower <= 3) count--;
1620 // saved in big endian format
1621 ulong[] mybig = bigbuf[0 .. count];
1623 if (lower < T.mant_dig)
1625 mybig[0] = mnt >> lower;
1626 mybig[1] = (mnt & ((1L << lower) - 1)) << 60 - lower;
1628 else
1629 mybig[0] = (mnt & ((1L << lower) - 1)) << 60 - lower;
1631 // Generation of digits by consecutive division with reminder by 10.
1632 int msu = 0; // Most significant ulong; when it get's zero, we can ignore it furtheron
1633 while (msu < count - 1 || mybig[$ - 1] != 0)
1635 ulong mod = 0;
1636 foreach (i;msu .. count)
1638 mybig[i] |= mod << 60;
1639 mod = mybig[i] % 10;
1640 mybig[i] /= 10;
1642 if (mybig[msu] == 0)
1643 ++msu;
1645 dec_buf[--left] = cast(byte) ('0' + mod);
1648 rc = RoundingClass.ZERO;
1650 else if (exp < small_bound)
1652 // small number without integer digits
1654 // Again this number does not fit in a ulong and we use an array of ulongs. And again we
1655 // only use 60 bits, because this simplifies the multiplication by 10.
1656 int count = (T.mant_dig - exp - 2) / 60 + 1;
1658 // saved in little endian format
1659 ulong[] mybig = bigbuf[0 .. count];
1661 // only the last few ulongs contain the mantiassa. Because of little endian
1662 // format these are the ulongs at index 0 and 1 (and 2 in case of x87 reals).
1663 // The rest are zeros.
1664 int upper = 60 - (-exp - 1) % 60;
1666 static if (is(T == real) && real.mant_dig == 64)
1668 if (upper < 4)
1670 mybig[0] = (mnt & ((1L << (4 - upper)) - 1)) << 56 + upper;
1671 mybig[1] = (mnt >> (4 - upper)) & ((1L << 60) - 1);
1672 mybig[2] = mnt >> 64 - upper;
1674 else
1676 mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
1677 mybig[1] = mnt >> (T.mant_dig - upper);
1680 else
1682 if (upper < T.mant_dig)
1684 mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
1685 mybig[1] = mnt >> (T.mant_dig - upper);
1687 else
1688 mybig[0] = mnt << (upper - T.mant_dig);
1691 dec_buf[--left] = '0'; // 0 left of the dot
1692 dec_buf[right++] = '.';
1694 static if (g)
1696 // precision starts at first non zero, so we move start
1697 // to the right, until we found first non zero, thus avoiding
1698 // a premature break of the loop
1699 bool found = false;
1700 start = left + 1;
1703 // Generation of digits by consecutive multiplication by 10.
1704 int lsu = 0; // Least significant ulong; when it get's zero, we can ignore it furtheron
1705 while ((lsu < count - 1 || mybig[$ - 1] != 0) && right - start - 1 < f.precision)
1707 ulong over = 0;
1708 foreach (i;lsu .. count)
1710 mybig[i] = mybig[i] * 10 + over;
1711 over = mybig[i] >> 60;
1712 mybig[i] &= (1L << 60) - 1;
1714 if (mybig[lsu] == 0)
1715 ++lsu;
1717 dec_buf[right++] = cast(byte) ('0' + over);
1719 static if (g)
1721 if (dec_buf[right - 1] != '0')
1722 found = true;
1723 else if (!found)
1724 start++;
1728 static if (g) start = 2;
1730 if (lsu >= count - 1 && mybig[count - 1] == 0)
1731 rc = RoundingClass.ZERO;
1732 else if (lsu == count - 1 && mybig[lsu] == 1L << 59)
1733 rc = RoundingClass.FIVE;
1734 else
1736 ulong over = 0;
1737 foreach (i;lsu .. count)
1739 mybig[i] = mybig[i] * 10 + over;
1740 over = mybig[i] >> 60;
1741 mybig[i] &= (1L << 60) - 1;
1743 rc = over >= 5 ? RoundingClass.UPPER : RoundingClass.LOWER;
1746 else
1748 // medium sized number, probably with integer and fractional digits
1749 // this is fastest, because both parts fit into a ulong each
1750 ulong int_part = mnt >> (T.mant_dig - 1 - exp);
1751 ulong frac_part = mnt & ((1L << (T.mant_dig - 1 - exp)) - 1);
1753 // for x87 reals the mantiassa might be up to 3 bits too long
1754 // we need to save these bits as a tail and handle this separately
1755 static if (is(T == real) && real.mant_dig == 64)
1757 ulong tail = 0;
1758 ulong tail_length = 0;
1759 if (exp < 3)
1761 tail = frac_part & ((1L << (3 - exp)) - 1);
1762 tail_length = 3 - exp;
1763 frac_part >>= 3 - exp;
1764 exp = 3;
1768 static if (g) auto found = int_part > 0; // searching first non zero
1770 // creating int part
1771 if (int_part == 0)
1772 dec_buf[--left] = '0';
1773 else
1775 import core.bitop : bsr;
1776 left = right = start = int_part.bsr * 100 / 332 + 4;
1778 while (int_part > 0)
1780 dec_buf[--left] = '0' + (int_part % 10);
1781 int_part /= 10;
1785 static if (g) size_t save_start = right;
1787 dec_buf[right++] = '.';
1789 // creating frac part
1790 static if (g) start = left + (found ? 0 : 1);
1791 while (frac_part != 0 && right - start - 1 < f.precision)
1793 frac_part *= 10;
1794 static if (is(T == real) && real.mant_dig == 64)
1796 if (tail_length > 0)
1798 // together this is *= 10;
1799 tail *= 5;
1800 tail_length--;
1802 frac_part += tail >> tail_length;
1803 if (tail_length > 0)
1804 tail &= (1L << tail_length) - 1;
1807 dec_buf[right++] = cast(byte)('0' + (frac_part >> (T.mant_dig - 1 - exp)));
1809 static if (g)
1811 if (dec_buf[right - 1] != '0')
1812 found = true;
1813 else if (!found)
1814 start++;
1817 frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
1820 static if (g) start = save_start;
1822 if (frac_part == 0)
1823 rc = RoundingClass.ZERO;
1824 else
1826 frac_part *= 10;
1827 auto nextDigit = frac_part >> (T.mant_dig - 1 - exp);
1828 frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
1830 if (nextDigit == 5 && frac_part == 0)
1831 rc = RoundingClass.FIVE;
1832 else if (nextDigit >= 5)
1833 rc = RoundingClass.UPPER;
1834 else
1835 rc = RoundingClass.LOWER;
1839 if (round(dec_buf, left, right, rc, sgn == "-")) left--;
1841 while (right > start + 1 && dec_buf[right - 1] == '0') right--;
1843 static if (g)
1844 writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.allDigits);
1845 else
1846 writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.fractionalDigits);
1849 @safe unittest
1851 auto f = FormatSpec!dchar("");
1852 f.spec = 'f';
1853 assert(printFloat(float.nan, f) == "nan");
1854 assert(printFloat(-float.nan, f) == "-nan");
1855 assert(printFloat(float.infinity, f) == "inf");
1856 assert(printFloat(-float.infinity, f) == "-inf");
1857 assert(printFloat(0.0f, f) == "0.000000");
1858 assert(printFloat(-0.0f, f) == "-0.000000");
1859 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1860 assert(printFloat(cast(float) 1e-40, f) == "0.000000");
1861 assert(printFloat(cast(float) -1e-40, f) == "-0.000000");
1862 assert(printFloat(1e-30f, f) == "0.000000");
1863 assert(printFloat(-1e-30f, f) == "-0.000000");
1864 assert(printFloat(1e-10f, f) == "0.000000");
1865 assert(printFloat(-1e-10f, f) == "-0.000000");
1866 assert(printFloat(0.1f, f) == "0.100000");
1867 assert(printFloat(-0.1f, f) == "-0.100000");
1868 assert(printFloat(10.0f, f) == "10.000000");
1869 assert(printFloat(-10.0f, f) == "-10.000000");
1870 assert(printFloat(1e30f, f) == "1000000015047466219876688855040.000000");
1871 assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.000000");
1873 import std.math.operations : nextUp, nextDown;
1874 assert(printFloat(nextUp(0.0f), f) == "0.000000");
1875 assert(printFloat(nextDown(-0.0f), f) == "-0.000000");
1878 @safe unittest
1880 auto f = FormatSpec!dchar("");
1881 f.spec = 'f';
1882 f.width = 20;
1883 f.precision = 10;
1885 assert(printFloat(float.nan, f) == " nan");
1886 assert(printFloat(-float.nan, f) == " -nan");
1887 assert(printFloat(float.infinity, f) == " inf");
1888 assert(printFloat(-float.infinity, f) == " -inf");
1889 assert(printFloat(0.0f, f) == " 0.0000000000");
1890 assert(printFloat(-0.0f, f) == " -0.0000000000");
1891 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1892 assert(printFloat(cast(float) 1e-40, f) == " 0.0000000000");
1893 assert(printFloat(cast(float) -1e-40, f) == " -0.0000000000");
1894 assert(printFloat(1e-30f, f) == " 0.0000000000");
1895 assert(printFloat(-1e-30f, f) == " -0.0000000000");
1896 assert(printFloat(1e-10f, f) == " 0.0000000001");
1897 assert(printFloat(-1e-10f, f) == " -0.0000000001");
1898 assert(printFloat(0.1f, f) == " 0.1000000015");
1899 assert(printFloat(-0.1f, f) == " -0.1000000015");
1900 assert(printFloat(10.0f, f) == " 10.0000000000");
1901 assert(printFloat(-10.0f, f) == " -10.0000000000");
1902 assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000");
1903 assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000");
1905 import std.math.operations : nextUp, nextDown;
1906 assert(printFloat(nextUp(0.0f), f) == " 0.0000000000");
1907 assert(printFloat(nextDown(-0.0f), f) == " -0.0000000000");
1910 @safe unittest
1912 auto f = FormatSpec!dchar("");
1913 f.spec = 'f';
1914 f.width = 20;
1915 f.precision = 10;
1916 f.flDash = true;
1918 assert(printFloat(float.nan, f) == "nan ");
1919 assert(printFloat(-float.nan, f) == "-nan ");
1920 assert(printFloat(float.infinity, f) == "inf ");
1921 assert(printFloat(-float.infinity, f) == "-inf ");
1922 assert(printFloat(0.0f, f) == "0.0000000000 ");
1923 assert(printFloat(-0.0f, f) == "-0.0000000000 ");
1924 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1925 assert(printFloat(cast(float) 1e-40, f) == "0.0000000000 ");
1926 assert(printFloat(cast(float) -1e-40, f) == "-0.0000000000 ");
1927 assert(printFloat(1e-30f, f) == "0.0000000000 ");
1928 assert(printFloat(-1e-30f, f) == "-0.0000000000 ");
1929 assert(printFloat(1e-10f, f) == "0.0000000001 ");
1930 assert(printFloat(-1e-10f, f) == "-0.0000000001 ");
1931 assert(printFloat(0.1f, f) == "0.1000000015 ");
1932 assert(printFloat(-0.1f, f) == "-0.1000000015 ");
1933 assert(printFloat(10.0f, f) == "10.0000000000 ");
1934 assert(printFloat(-10.0f, f) == "-10.0000000000 ");
1935 assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000");
1936 assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000");
1938 import std.math.operations : nextUp, nextDown;
1939 assert(printFloat(nextUp(0.0f), f) == "0.0000000000 ");
1940 assert(printFloat(nextDown(-0.0f), f) == "-0.0000000000 ");
1943 @safe unittest
1945 auto f = FormatSpec!dchar("");
1946 f.spec = 'f';
1947 f.width = 20;
1948 f.precision = 10;
1949 f.flZero = true;
1951 assert(printFloat(float.nan, f) == " nan");
1952 assert(printFloat(-float.nan, f) == " -nan");
1953 assert(printFloat(float.infinity, f) == " inf");
1954 assert(printFloat(-float.infinity, f) == " -inf");
1955 assert(printFloat(0.0f, f) == "000000000.0000000000");
1956 assert(printFloat(-0.0f, f) == "-00000000.0000000000");
1957 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
1958 assert(printFloat(cast(float) 1e-40, f) == "000000000.0000000000");
1959 assert(printFloat(cast(float) -1e-40, f) == "-00000000.0000000000");
1960 assert(printFloat(1e-30f, f) == "000000000.0000000000");
1961 assert(printFloat(-1e-30f, f) == "-00000000.0000000000");
1962 assert(printFloat(1e-10f, f) == "000000000.0000000001");
1963 assert(printFloat(-1e-10f, f) == "-00000000.0000000001");
1964 assert(printFloat(0.1f, f) == "000000000.1000000015");
1965 assert(printFloat(-0.1f, f) == "-00000000.1000000015");
1966 assert(printFloat(10.0f, f) == "000000010.0000000000");
1967 assert(printFloat(-10.0f, f) == "-00000010.0000000000");
1968 assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000");
1969 assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000");
1971 import std.math.operations : nextUp, nextDown;
1972 assert(printFloat(nextUp(0.0f), f) == "000000000.0000000000");
1973 assert(printFloat(nextDown(-0.0f), f) == "-00000000.0000000000");
1976 @safe unittest
1978 import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
1980 // std.math's FloatingPointControl isn't available on all target platforms
1981 static if (is(FloatingPointControl))
1983 FloatingPointControl fpctrl;
1985 auto f = FormatSpec!dchar("");
1986 f.spec = 'f';
1987 f.precision = 0;
1989 fpctrl.rounding = FloatingPointControl.roundToNearest;
1992 assert(printFloat(11.5f, f) == "12");
1993 assert(printFloat(12.5f, f) == "13");
1994 assert(printFloat(11.7f, f) == "12");
1995 assert(printFloat(11.3f, f) == "11");
1996 assert(printFloat(11.0f, f) == "11");
1997 assert(printFloat(-11.5f, f) == "-12");
1998 assert(printFloat(-12.5f, f) == "-13");
1999 assert(printFloat(-11.7f, f) == "-12");
2000 assert(printFloat(-11.3f, f) == "-11");
2001 assert(printFloat(-11.0f, f) == "-11");
2004 assert(printFloat(11.5f, f) == "12");
2005 assert(printFloat(12.5f, f) == "12");
2006 assert(printFloat(11.7f, f) == "12");
2007 assert(printFloat(11.3f, f) == "11");
2008 assert(printFloat(11.0f, f) == "11");
2009 assert(printFloat(-11.5f, f) == "-12");
2010 assert(printFloat(-12.5f, f) == "-12");
2011 assert(printFloat(-11.7f, f) == "-12");
2012 assert(printFloat(-11.3f, f) == "-11");
2013 assert(printFloat(-11.0f, f) == "-11");
2015 fpctrl.rounding = FloatingPointControl.roundToZero;
2017 assert(printFloat(11.5f, f) == "11");
2018 assert(printFloat(12.5f, f) == "12");
2019 assert(printFloat(11.7f, f) == "11");
2020 assert(printFloat(11.3f, f) == "11");
2021 assert(printFloat(11.0f, f) == "11");
2022 assert(printFloat(-11.5f, f) == "-11");
2023 assert(printFloat(-12.5f, f) == "-12");
2024 assert(printFloat(-11.7f, f) == "-11");
2025 assert(printFloat(-11.3f, f) == "-11");
2026 assert(printFloat(-11.0f, f) == "-11");
2028 fpctrl.rounding = FloatingPointControl.roundUp;
2030 assert(printFloat(11.5f, f) == "12");
2031 assert(printFloat(12.5f, f) == "13");
2032 assert(printFloat(11.7f, f) == "12");
2033 assert(printFloat(11.3f, f) == "12");
2034 assert(printFloat(11.0f, f) == "11");
2035 assert(printFloat(-11.5f, f) == "-11");
2036 assert(printFloat(-12.5f, f) == "-12");
2037 assert(printFloat(-11.7f, f) == "-11");
2038 assert(printFloat(-11.3f, f) == "-11");
2039 assert(printFloat(-11.0f, f) == "-11");
2041 fpctrl.rounding = FloatingPointControl.roundDown;
2043 assert(printFloat(11.5f, f) == "11");
2044 assert(printFloat(12.5f, f) == "12");
2045 assert(printFloat(11.7f, f) == "11");
2046 assert(printFloat(11.3f, f) == "11");
2047 assert(printFloat(11.0f, f) == "11");
2048 assert(printFloat(-11.5f, f) == "-12");
2049 assert(printFloat(-12.5f, f) == "-13");
2050 assert(printFloat(-11.7f, f) == "-12");
2051 assert(printFloat(-11.3f, f) == "-12");
2052 assert(printFloat(-11.0f, f) == "-11");
2056 @safe unittest
2058 auto f = FormatSpec!dchar("");
2059 f.spec = 'f';
2060 assert(printFloat(double.nan, f) == "nan");
2061 assert(printFloat(-double.nan, f) == "-nan");
2062 assert(printFloat(double.infinity, f) == "inf");
2063 assert(printFloat(-double.infinity, f) == "-inf");
2064 assert(printFloat(0.0, f) == "0.000000");
2065 assert(printFloat(-0.0, f) == "-0.000000");
2066 // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2067 assert(printFloat(1e-307 / 1000, f) == "0.000000");
2068 assert(printFloat(-1e-307 / 1000, f) == "-0.000000");
2069 assert(printFloat(1e-30, f) == "0.000000");
2070 assert(printFloat(-1e-30, f) == "-0.000000");
2071 assert(printFloat(1e-10, f) == "0.000000");
2072 assert(printFloat(-1e-10, f) == "-0.000000");
2073 assert(printFloat(0.1, f) == "0.100000");
2074 assert(printFloat(-0.1, f) == "-0.100000");
2075 assert(printFloat(10.0, f) == "10.000000");
2076 assert(printFloat(-10.0, f) == "-10.000000");
2077 assert(printFloat(1e300, f) ==
2078 "100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786"
2079 ~"404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936"
2080 ~"895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054"
2081 ~"0160.000000");
2082 assert(printFloat(-1e300, f) ==
2083 "-100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786"
2084 ~"404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936"
2085 ~"895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054"
2086 ~"0160.000000");
2088 import std.math.operations : nextUp, nextDown;
2089 assert(printFloat(nextUp(0.0), f) == "0.000000");
2090 assert(printFloat(nextDown(-0.0), f) == "-0.000000");
2093 @safe unittest
2095 static if (real.mant_dig > 64)
2097 pragma(msg, "printFloat tests disabled because of unsupported `real` format");
2099 else
2101 auto f = FormatSpec!dchar("");
2102 f.spec = 'f';
2103 assert(printFloat(real.nan, f) == "nan");
2104 assert(printFloat(-real.nan, f) == "-nan");
2105 assert(printFloat(real.infinity, f) == "inf");
2106 assert(printFloat(-real.infinity, f) == "-inf");
2107 assert(printFloat(0.0L, f) == "0.000000");
2108 assert(printFloat(-0.0L, f) == "-0.000000");
2111 static if (real.mant_dig == 64)
2113 assert(printFloat(1e-4940L, f) == "0.000000");
2114 assert(printFloat(-1e-4940L, f) == "-0.000000");
2115 assert(printFloat(1e-30L, f) == "0.000000");
2116 assert(printFloat(-1e-30L, f) == "-0.000000");
2117 assert(printFloat(1e-10L, f) == "0.000000");
2118 assert(printFloat(-1e-10L, f) == "-0.000000");
2119 assert(printFloat(0.1L, f) == "0.100000");
2120 assert(printFloat(-0.1L, f) == "-0.100000");
2121 assert(printFloat(10.0L, f) == "10.000000");
2122 assert(printFloat(-10.0L, f) == "-10.000000");
2123 version (Windows) {} // issue 20972
2124 else
2126 auto result1 = printFloat(1e4000L, f);
2127 assert(result1.length == 4007 && result1[0 .. 40] == "9999999999999999999965463873099623784932");
2128 auto result2 = printFloat(-1e4000L, f);
2129 assert(result2.length == 4008 && result2[0 .. 40] == "-999999999999999999996546387309962378493");
2132 import std.math.operations : nextUp, nextDown;
2133 assert(printFloat(nextUp(0.0L), f) == "0.000000");
2134 assert(printFloat(nextDown(-0.0L), f) == "-0.000000");
2138 @safe unittest
2140 import std.exception : assertCTFEable;
2141 import std.math.exponential : log2;
2142 import std.math.operations : nextDown;
2144 assertCTFEable!(
2146 // log2 is broken for x87-reals on some computers in CTFE
2147 // the following tests excludes these computers from the tests
2148 // (issue 21757)
2149 enum test = cast(int) log2(3.05e2312L);
2150 static if (real.mant_dig == 64 && test == 7681)
2152 auto f = FormatSpec!dchar("");
2153 f.spec = 'f';
2154 assert(printFloat(real.infinity, f) == "inf");
2155 assert(printFloat(10.0L, f) == "10.000000");
2156 assert(printFloat(2.6080L, f) == "2.608000");
2157 auto result1 = printFloat(3.05e2312L, f);
2158 assert(result1.length == 2320);
2159 assert(result1[0 .. 20] == "30499999999999999999");
2161 f.precision = 60;
2162 assert(printFloat(2.65e-54L, f) ==
2163 "0.000000000000000000000000000000000000000000000000000002650000");
2166 commented out, because CTFE is currently too slow for 5000 digits with extreme values
2168 f.precision = 5000;
2169 auto result2 = printFloat(1.2119e-4822L, f);
2170 assert(result2.length == 5002);
2171 assert(result2[$ - 20 .. $] == "60076763752233836613");
2172 auto result3 = printFloat(real.min_normal, f);
2173 assert(result3.length == 5002);
2174 assert(result3[$ - 20 .. $] == "47124010882722980874");
2175 auto result4 = printFloat(real.min_normal.nextDown, f);
2176 assert(result4.length == 5002);
2177 assert(result4[$ - 20 .. $] == "52925846892214823939");
2183 @safe unittest
2185 auto f = FormatSpec!dchar("");
2186 f.spec = 'f';
2188 import std.math.operations : nextUp;
2190 double eps = nextUp(0.0);
2191 f.precision = 1000;
2192 assert(printFloat(eps, f) ==
2193 "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
2194 ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
2195 ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
2196 ~"00000000000000000000000000000049406564584124654417656879286822137236505980261432476442558568250067"
2197 ~"55072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131"
2198 ~"90311404527845817167848982103688718636056998730723050006387409153564984387312473397273169615140031"
2199 ~"71538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012"
2200 ~"97099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107"
2201 ~"49170333222684475333572083243193609238289345836806010601150616980975307834227731832924790498252473"
2202 ~"07763759272478746560847782037344696995336470179726777175851256605511991315048911014510378627381672"
2203 ~"509558373897335989937");
2205 f.precision = 0;
2206 assert(printFloat(double.max, f) ==
2207 "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878"
2208 ~"17154045895351438246423432132688946418276846754670353751698604991057655128207624549009038932894407"
2209 ~"58685084551339423045832369032229481658085593321233482747978262041447231687381771809192998812504040"
2210 ~"26184124858368");
2212 f.precision = 50;
2213 assert(printFloat(double.epsilon, f) ==
2214 "0.00000000000000022204460492503130808472633361816406");
2216 f.precision = 10;
2217 assert(printFloat(1.0/3.0, f) == "0.3333333333");
2218 assert(printFloat(1.0/7.0, f) == "0.1428571429");
2219 assert(printFloat(1.0/9.0, f) == "0.1111111111");
2222 @safe unittest
2224 auto f = FormatSpec!dchar("");
2225 f.spec = 'f';
2226 f.precision = 15;
2228 import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
2229 LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
2231 assert(printFloat(cast(double) E, f) == "2.718281828459045");
2232 assert(printFloat(cast(double) PI, f) == "3.141592653589793");
2233 assert(printFloat(cast(double) PI_2, f) == "1.570796326794897");
2234 assert(printFloat(cast(double) PI_4, f) == "0.785398163397448");
2235 assert(printFloat(cast(double) M_1_PI, f) == "0.318309886183791");
2236 assert(printFloat(cast(double) M_2_PI, f) == "0.636619772367581");
2237 assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.128379167095513");
2238 assert(printFloat(cast(double) LN10, f) == "2.302585092994046");
2239 assert(printFloat(cast(double) LN2, f) == "0.693147180559945");
2240 assert(printFloat(cast(double) LOG2, f) == "0.301029995663981");
2241 assert(printFloat(cast(double) LOG2E, f) == "1.442695040888963");
2242 assert(printFloat(cast(double) LOG2T, f) == "3.321928094887362");
2243 assert(printFloat(cast(double) LOG10E, f) == "0.434294481903252");
2244 assert(printFloat(cast(double) SQRT2, f) == "1.414213562373095");
2245 assert(printFloat(cast(double) SQRT1_2, f) == "0.707106781186548");
2248 // for 100% coverage
2249 @safe unittest
2251 auto f = FormatSpec!dchar("");
2252 f.spec = 'f';
2253 f.precision = 1;
2254 assert(printFloat(9.99, f) == "10.0");
2256 import std.math.operations : nextUp;
2258 float eps = nextUp(0.0f);
2260 f.precision = 148;
2261 assert(printFloat(eps, f) ==
2262 "0.0000000000000000000000000000000000000000000014012984643248170709237295832899161312802619418765157"
2263 ~"717570682838897910826858606014866381883621215820312");
2265 f.precision = 149;
2266 assert(printFloat(eps, f) ==
2267 "0.0000000000000000000000000000000000000000000014012984643248170709237295832899161312802619418765157"
2268 ~"7175706828388979108268586060148663818836212158203125");
2271 private void printFloatG(Writer, T, Char)(auto ref Writer w, const(T) val,
2272 FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
2273 if (is(T == float) || is(T == double)
2274 || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
2276 import core.math : abs = fabs;
2278 if (f.precision == f.UNSPECIFIED)
2279 f.precision = 6;
2281 if (f.precision == 0)
2282 f.precision = 1;
2284 import std.math.hardware;
2285 import std.format.internal.write : RoundingMode;
2287 auto rm = RoundingMode.toNearestTiesToEven;
2289 if (!__ctfe)
2291 // std.math's FloatingPointControl isn't available on all target platforms
2292 static if (is(FloatingPointControl))
2294 switch (FloatingPointControl.rounding)
2296 case FloatingPointControl.roundUp:
2297 rm = RoundingMode.up;
2298 break;
2299 case FloatingPointControl.roundDown:
2300 rm = RoundingMode.down;
2301 break;
2302 case FloatingPointControl.roundToZero:
2303 rm = RoundingMode.toZero;
2304 break;
2305 case FloatingPointControl.roundToNearest:
2306 rm = RoundingMode.toNearestTiesToEven;
2307 break;
2308 default: assert(false, "Unknown floating point rounding mode");
2313 bool useE = false;
2315 final switch (rm)
2317 case RoundingMode.up:
2318 useE = abs(val) >= 10.0 ^^ f.precision - (val > 0 ? 1 : 0)
2319 || abs(val) < 0.0001 - (val > 0 ? (10.0 ^^ (-4 - f.precision)) : 0);
2320 break;
2321 case RoundingMode.down:
2322 useE = abs(val) >= 10.0 ^^ f.precision - (val < 0 ? 1 : 0)
2323 || abs(val) < 0.0001 - (val < 0 ? (10.0 ^^ (-4 - f.precision)) : 0);
2324 break;
2325 case RoundingMode.toZero:
2326 useE = abs(val) >= 10.0 ^^ f.precision
2327 || abs(val) < 0.0001;
2328 break;
2329 case RoundingMode.toNearestTiesToEven:
2330 case RoundingMode.toNearestTiesAwayFromZero:
2331 useE = abs(val) >= 10.0 ^^ f.precision - 0.5
2332 || abs(val) < 0.0001 - 0.5 * (10.0 ^^ (-4 - f.precision));
2333 break;
2336 if (useE)
2337 return printFloatE!true(w, val, f, sgn, exp, mnt, is_upper);
2338 else
2339 return printFloatF!true(w, val, f, sgn, exp, mnt, is_upper);
2342 @safe unittest
2344 // This one tests the switch between e-like and f-like output.
2345 // There is a small gap left between the two, where the used
2346 // variation is not clearly defined. This is intentional and due
2347 // to the way, D handles floating point numbers. On different
2348 // computers with different reals the results may vary in this gap.
2350 import std.math.operations : nextDown, nextUp;
2351 import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
2353 auto f = FormatSpec!dchar("");
2354 f.spec = 'g';
2356 double val = 999999.5;
2357 assert(printFloat(val.nextUp, f) == "1e+06");
2358 val = nextDown(val);
2359 assert(printFloat(val.nextDown, f) == "999999");
2361 val = 0.00009999995;
2362 assert(printFloat(val.nextUp, f) == "0.0001");
2363 val = nextDown(val);
2364 assert(printFloat(val.nextDown, f) == "9.99999e-05");
2366 static if (is(FloatingPointControl))
2368 FloatingPointControl fpctrl;
2370 fpctrl.rounding = FloatingPointControl.roundToZero;
2372 val = 1000000;
2373 assert(printFloat(val.nextUp, f) == "1e+06");
2374 val = nextDown(val);
2375 assert(printFloat(val.nextDown, f) == "999999");
2377 val = 0.0001;
2378 assert(printFloat(val.nextUp, f) == "0.0001");
2379 val = nextDown(val);
2380 assert(printFloat(val.nextDown, f) == "9.99999e-05");
2382 fpctrl.rounding = FloatingPointControl.roundUp;
2384 val = 999999;
2385 assert(printFloat(val.nextUp, f) == "1e+06");
2386 val = nextDown(val);
2387 assert(printFloat(val.nextDown, f) == "999999");
2389 // 0.0000999999 is actually represented as 0.0000999998999..., which is
2390 // less than 0.0000999999, so we need to use nextUp to get the corner case here
2391 val = nextUp(0.0000999999);
2392 assert(printFloat(val.nextUp, f) == "0.0001");
2393 val = nextDown(val);
2394 assert(printFloat(val.nextDown, f) == "9.99999e-05");
2396 fpctrl.rounding = FloatingPointControl.roundDown;
2398 val = 1000000;
2399 assert(printFloat(val.nextUp, f) == "1e+06");
2400 val = nextDown(val);
2401 assert(printFloat(val.nextDown, f) == "999999");
2403 val = 0.0001;
2404 assert(printFloat(val.nextUp, f) == "0.0001");
2405 val = nextDown(val);
2406 assert(printFloat(val.nextDown, f) == "9.99999e-05");
2410 @safe unittest
2412 auto f = FormatSpec!dchar("");
2413 f.spec = 'g';
2414 assert(printFloat(float.nan, f) == "nan");
2415 assert(printFloat(-float.nan, f) == "-nan");
2416 assert(printFloat(float.infinity, f) == "inf");
2417 assert(printFloat(-float.infinity, f) == "-inf");
2418 assert(printFloat(0.0f, f) == "0");
2419 assert(printFloat(-0.0f, f) == "-0");
2421 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2422 assert(printFloat(cast(float) 1e-40, f) == "9.99995e-41");
2423 assert(printFloat(cast(float) -1e-40, f) == "-9.99995e-41");
2424 assert(printFloat(1e-30f, f) == "1e-30");
2425 assert(printFloat(-1e-30f, f) == "-1e-30");
2426 assert(printFloat(1e-10f, f) == "1e-10");
2427 assert(printFloat(-1e-10f, f) == "-1e-10");
2428 assert(printFloat(0.1f, f) == "0.1");
2429 assert(printFloat(-0.1f, f) == "-0.1");
2430 assert(printFloat(10.0f, f) == "10");
2431 assert(printFloat(-10.0f, f) == "-10");
2432 assert(printFloat(1e30f, f) == "1e+30");
2433 assert(printFloat(-1e30f, f) == "-1e+30");
2435 import std.math.operations : nextUp, nextDown;
2436 assert(printFloat(nextUp(0.0f), f) == "1.4013e-45");
2437 assert(printFloat(nextDown(-0.0f), f) == "-1.4013e-45");
2440 @safe unittest
2442 auto f = FormatSpec!dchar("");
2443 f.spec = 'g';
2444 f.width = 20;
2445 f.precision = 10;
2447 assert(printFloat(float.nan, f) == " nan");
2448 assert(printFloat(-float.nan, f) == " -nan");
2449 assert(printFloat(float.infinity, f) == " inf");
2450 assert(printFloat(-float.infinity, f) == " -inf");
2451 assert(printFloat(0.0f, f) == " 0");
2452 assert(printFloat(-0.0f, f) == " -0");
2453 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2454 assert(printFloat(cast(float) 1e-40, f) == " 9.999946101e-41");
2455 assert(printFloat(cast(float) -1e-40, f) == " -9.999946101e-41");
2456 assert(printFloat(1e-30f, f) == " 1.000000003e-30");
2457 assert(printFloat(-1e-30f, f) == " -1.000000003e-30");
2458 assert(printFloat(1e-10f, f) == " 1.000000013e-10");
2459 assert(printFloat(-1e-10f, f) == " -1.000000013e-10");
2460 assert(printFloat(0.1f, f) == " 0.1000000015");
2461 assert(printFloat(-0.1f, f) == " -0.1000000015");
2462 assert(printFloat(10.0f, f) == " 10");
2463 assert(printFloat(-10.0f, f) == " -10");
2464 assert(printFloat(1e30f, f) == " 1.000000015e+30");
2465 assert(printFloat(-1e30f, f) == " -1.000000015e+30");
2467 import std.math.operations : nextUp, nextDown;
2468 assert(printFloat(nextUp(0.0f), f) == " 1.401298464e-45");
2469 assert(printFloat(nextDown(-0.0f), f) == " -1.401298464e-45");
2472 @safe unittest
2474 auto f = FormatSpec!dchar("");
2475 f.spec = 'g';
2476 f.width = 20;
2477 f.precision = 10;
2478 f.flDash = true;
2480 assert(printFloat(float.nan, f) == "nan ");
2481 assert(printFloat(-float.nan, f) == "-nan ");
2482 assert(printFloat(float.infinity, f) == "inf ");
2483 assert(printFloat(-float.infinity, f) == "-inf ");
2484 assert(printFloat(0.0f, f) == "0 ");
2485 assert(printFloat(-0.0f, f) == "-0 ");
2487 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2488 assert(printFloat(cast(float) 1e-40, f) == "9.999946101e-41 ");
2489 assert(printFloat(cast(float) -1e-40, f) == "-9.999946101e-41 ");
2490 assert(printFloat(1e-30f, f) == "1.000000003e-30 ");
2491 assert(printFloat(-1e-30f, f) == "-1.000000003e-30 ");
2492 assert(printFloat(1e-10f, f) == "1.000000013e-10 ");
2493 assert(printFloat(-1e-10f, f) == "-1.000000013e-10 ");
2494 assert(printFloat(0.1f, f) == "0.1000000015 ");
2495 assert(printFloat(-0.1f, f) == "-0.1000000015 ");
2496 assert(printFloat(10.0f, f) == "10 ");
2497 assert(printFloat(-10.0f, f) == "-10 ");
2498 assert(printFloat(1e30f, f) == "1.000000015e+30 ");
2499 assert(printFloat(-1e30f, f) == "-1.000000015e+30 ");
2501 import std.math.operations : nextUp, nextDown;
2502 assert(printFloat(nextUp(0.0f), f) == "1.401298464e-45 ");
2503 assert(printFloat(nextDown(-0.0f), f) == "-1.401298464e-45 ");
2506 @safe unittest
2508 auto f = FormatSpec!dchar("");
2509 f.spec = 'g';
2510 f.width = 20;
2511 f.precision = 10;
2512 f.flZero = true;
2514 assert(printFloat(float.nan, f) == " nan");
2515 assert(printFloat(-float.nan, f) == " -nan");
2516 assert(printFloat(float.infinity, f) == " inf");
2517 assert(printFloat(-float.infinity, f) == " -inf");
2518 assert(printFloat(0.0f, f) == "00000000000000000000");
2519 assert(printFloat(-0.0f, f) == "-0000000000000000000");
2521 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2522 assert(printFloat(cast(float) 1e-40, f) == "000009.999946101e-41");
2523 assert(printFloat(cast(float) -1e-40, f) == "-00009.999946101e-41");
2524 assert(printFloat(1e-30f, f) == "000001.000000003e-30");
2525 assert(printFloat(-1e-30f, f) == "-00001.000000003e-30");
2526 assert(printFloat(1e-10f, f) == "000001.000000013e-10");
2527 assert(printFloat(-1e-10f, f) == "-00001.000000013e-10");
2528 assert(printFloat(0.1f, f) == "000000000.1000000015");
2529 assert(printFloat(-0.1f, f) == "-00000000.1000000015");
2530 assert(printFloat(10.0f, f) == "00000000000000000010");
2531 assert(printFloat(-10.0f, f) == "-0000000000000000010");
2532 assert(printFloat(1e30f, f) == "000001.000000015e+30");
2533 assert(printFloat(-1e30f, f) == "-00001.000000015e+30");
2535 import std.math.operations : nextUp, nextDown;
2536 assert(printFloat(nextUp(0.0f), f) == "000001.401298464e-45");
2537 assert(printFloat(nextDown(-0.0f), f) == "-00001.401298464e-45");
2540 @safe unittest
2542 auto f = FormatSpec!dchar("");
2543 f.spec = 'g';
2544 f.precision = 10;
2545 f.flHash = true;
2547 assert(printFloat(float.nan, f) == "nan");
2548 assert(printFloat(-float.nan, f) == "-nan");
2549 assert(printFloat(float.infinity, f) == "inf");
2550 assert(printFloat(-float.infinity, f) == "-inf");
2551 assert(printFloat(0.0f, f) == "0.000000000");
2552 assert(printFloat(-0.0f, f) == "-0.000000000");
2554 // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2555 assert(printFloat(cast(float) 1e-40, f) == "9.999946101e-41");
2556 assert(printFloat(cast(float) -1e-40, f) == "-9.999946101e-41");
2557 assert(printFloat(1e-30f, f) == "1.000000003e-30");
2558 assert(printFloat(-1e-30f, f) == "-1.000000003e-30");
2559 assert(printFloat(1e-10f, f) == "1.000000013e-10");
2560 assert(printFloat(-1e-10f, f) == "-1.000000013e-10");
2561 assert(printFloat(0.1f, f) == "0.1000000015");
2562 assert(printFloat(-0.1f, f) == "-0.1000000015");
2563 assert(printFloat(10.0f, f) == "10.00000000");
2564 assert(printFloat(-10.0f, f) == "-10.00000000");
2565 assert(printFloat(1e30f, f) == "1.000000015e+30");
2566 assert(printFloat(-1e30f, f) == "-1.000000015e+30");
2568 import std.math.operations : nextUp, nextDown;
2569 assert(printFloat(nextUp(0.0f), f) == "1.401298464e-45");
2570 assert(printFloat(nextDown(-0.0f), f) == "-1.401298464e-45");
2573 @safe unittest
2575 import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
2577 // std.math's FloatingPointControl isn't available on all target platforms
2578 static if (is(FloatingPointControl))
2580 FloatingPointControl fpctrl;
2582 char[256] buf;
2583 auto f = FormatSpec!dchar("");
2584 f.spec = 'g';
2585 f.precision = 2;
2587 fpctrl.rounding = FloatingPointControl.roundToNearest;
2590 assert(printFloat(11.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "12");
2591 assert(printFloat(12.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "13");
2592 assert(printFloat(11.7f, f, RoundingMode.toNearestTiesAwayFromZero) == "12");
2593 assert(printFloat(11.3f, f, RoundingMode.toNearestTiesAwayFromZero) == "11");
2594 assert(printFloat(11.0f, f, RoundingMode.toNearestTiesAwayFromZero) == "11");
2595 assert(printFloat(-11.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "-12");
2596 assert(printFloat(-12.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "-13");
2597 assert(printFloat(-11.7f, f, RoundingMode.toNearestTiesAwayFromZero) == "-12");
2598 assert(printFloat(-11.3f, f, RoundingMode.toNearestTiesAwayFromZero) == "-11");
2599 assert(printFloat(-11.0f, f, RoundingMode.toNearestTiesAwayFromZero) == "-11");
2602 // ties to even
2603 assert(printFloat(11.5f, f) == "12");
2604 assert(printFloat(12.5f, f) == "12");
2605 assert(printFloat(11.7f, f) == "12");
2606 assert(printFloat(11.3f, f) == "11");
2607 assert(printFloat(11.0f, f) == "11");
2608 assert(printFloat(-11.5f, f) == "-12");
2609 assert(printFloat(-12.5f, f) == "-12");
2610 assert(printFloat(-11.7f, f) == "-12");
2611 assert(printFloat(-11.3f, f) == "-11");
2612 assert(printFloat(-11.0f, f) == "-11");
2614 fpctrl.rounding = FloatingPointControl.roundToZero;
2616 assert(printFloat(11.5f, f) == "11");
2617 assert(printFloat(12.5f, f) == "12");
2618 assert(printFloat(11.7f, f) == "11");
2619 assert(printFloat(11.3f, f) == "11");
2620 assert(printFloat(11.0f, f) == "11");
2621 assert(printFloat(-11.5f, f) == "-11");
2622 assert(printFloat(-12.5f, f) == "-12");
2623 assert(printFloat(-11.7f, f) == "-11");
2624 assert(printFloat(-11.3f, f) == "-11");
2625 assert(printFloat(-11.0f, f) == "-11");
2627 fpctrl.rounding = FloatingPointControl.roundUp;
2629 assert(printFloat(11.5f, f) == "12");
2630 assert(printFloat(12.5f, f) == "13");
2631 assert(printFloat(11.7f, f) == "12");
2632 assert(printFloat(11.3f, f) == "12");
2633 assert(printFloat(11.0f, f) == "11");
2634 assert(printFloat(-11.5f, f) == "-11");
2635 assert(printFloat(-12.5f, f) == "-12");
2636 assert(printFloat(-11.7f, f) == "-11");
2637 assert(printFloat(-11.3f, f) == "-11");
2638 assert(printFloat(-11.0f, f) == "-11");
2640 fpctrl.rounding = FloatingPointControl.roundDown;
2642 assert(printFloat(11.5f, f) == "11");
2643 assert(printFloat(12.5f, f) == "12");
2644 assert(printFloat(11.7f, f) == "11");
2645 assert(printFloat(11.3f, f) == "11");
2646 assert(printFloat(11.0f, f) == "11");
2647 assert(printFloat(-11.5f, f) == "-12");
2648 assert(printFloat(-12.5f, f) == "-13");
2649 assert(printFloat(-11.7f, f) == "-12");
2650 assert(printFloat(-11.3f, f) == "-12");
2651 assert(printFloat(-11.0f, f) == "-11");
2655 @safe unittest
2657 auto f = FormatSpec!dchar("");
2658 f.spec = 'g';
2660 assert(printFloat(double.nan, f) == "nan");
2661 assert(printFloat(-double.nan, f) == "-nan");
2662 assert(printFloat(double.infinity, f) == "inf");
2663 assert(printFloat(-double.infinity, f) == "-inf");
2664 assert(printFloat(0.0, f) == "0");
2665 assert(printFloat(-0.0, f) == "-0");
2667 // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361
2668 assert(printFloat(1e-307 / 1000, f) == "1e-310");
2669 assert(printFloat(-1e-307 / 1000, f) == "-1e-310");
2670 assert(printFloat(1e-30, f) == "1e-30");
2671 assert(printFloat(-1e-30, f) == "-1e-30");
2672 assert(printFloat(1e-10, f) == "1e-10");
2673 assert(printFloat(-1e-10, f) == "-1e-10");
2674 assert(printFloat(0.1, f) == "0.1");
2675 assert(printFloat(-0.1, f) == "-0.1");
2676 assert(printFloat(10.0, f) == "10");
2677 assert(printFloat(-10.0, f) == "-10");
2678 assert(printFloat(1e300, f) == "1e+300");
2679 assert(printFloat(-1e300, f) == "-1e+300");
2681 import std.math.operations : nextUp, nextDown;
2682 assert(printFloat(nextUp(0.0), f) == "4.94066e-324");
2683 assert(printFloat(nextDown(-0.0), f) == "-4.94066e-324");
2686 @safe unittest
2688 static if (real.mant_dig > 64)
2690 pragma(msg, "printFloat tests disabled because of unsupported `real` format");
2692 else
2694 char[256] buf;
2695 auto f = FormatSpec!dchar("");
2696 f.spec = 'g';
2698 assert(printFloat(real.nan, f) == "nan");
2699 assert(printFloat(-real.nan, f) == "-nan");
2700 assert(printFloat(real.infinity, f) == "inf");
2701 assert(printFloat(-real.infinity, f) == "-inf");
2705 @safe unittest
2707 auto f = FormatSpec!dchar("");
2708 f.spec = 'g';
2710 import std.math.operations : nextUp;
2712 double eps = nextUp(0.0);
2713 f.precision = 1000;
2714 assert(printFloat(eps, f) ==
2715 "4.940656458412465441765687928682213723650598026143247644255856825006"
2716 ~ "755072702087518652998363616359923797965646954457177309266567103559"
2717 ~ "397963987747960107818781263007131903114045278458171678489821036887"
2718 ~ "186360569987307230500063874091535649843873124733972731696151400317"
2719 ~ "153853980741262385655911710266585566867681870395603106249319452715"
2720 ~ "914924553293054565444011274801297099995419319894090804165633245247"
2721 ~ "571478690147267801593552386115501348035264934720193790268107107491"
2722 ~ "703332226844753335720832431936092382893458368060106011506169809753"
2723 ~ "078342277318329247904982524730776375927247874656084778203734469699"
2724 ~ "533647017972677717585125660551199131504891101451037862738167250955"
2725 ~ "837389733598993664809941164205702637090279242767544565229087538682"
2726 ~ "506419718265533447265625e-324");
2728 f.precision = 50;
2729 assert(printFloat(double.max, f) ==
2730 "1.7976931348623157081452742373170435679807056752584e+308");
2731 assert(printFloat(double.epsilon, f) ==
2732 "2.220446049250313080847263336181640625e-16");
2734 f.precision = 10;
2735 assert(printFloat(1.0/3.0, f) == "0.3333333333");
2736 assert(printFloat(1.0/7.0, f) == "0.1428571429");
2737 assert(printFloat(1.0/9.0, f) == "0.1111111111");
2740 @safe unittest
2742 auto f = FormatSpec!dchar("");
2743 f.spec = 'g';
2744 f.precision = 15;
2746 import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
2747 LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
2749 assert(printFloat(cast(double) E, f) == "2.71828182845905");
2750 assert(printFloat(cast(double) PI, f) == "3.14159265358979");
2751 assert(printFloat(cast(double) PI_2, f) == "1.5707963267949");
2752 assert(printFloat(cast(double) PI_4, f) == "0.785398163397448");
2753 assert(printFloat(cast(double) M_1_PI, f) == "0.318309886183791");
2754 assert(printFloat(cast(double) M_2_PI, f) == "0.636619772367581");
2755 assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.12837916709551");
2756 assert(printFloat(cast(double) LN10, f) == "2.30258509299405");
2757 assert(printFloat(cast(double) LN2, f) == "0.693147180559945");
2758 assert(printFloat(cast(double) LOG2, f) == "0.301029995663981");
2759 assert(printFloat(cast(double) LOG2E, f) == "1.44269504088896");
2760 assert(printFloat(cast(double) LOG2T, f) == "3.32192809488736");
2761 assert(printFloat(cast(double) LOG10E, f) == "0.434294481903252");
2762 assert(printFloat(cast(double) SQRT2, f) == "1.4142135623731");
2763 assert(printFloat(cast(double) SQRT1_2, f) == "0.707106781186548");
2766 // for 100% coverage
2767 @safe unittest
2769 auto f = FormatSpec!dchar("");
2770 f.spec = 'g';
2771 f.precision = 0;
2773 assert(printFloat(0.009999, f) == "0.01");
2776 @safe unittest
2778 static if (real.mant_dig > 64)
2780 pragma(msg, "printFloat tests disabled because of unsupported `real` format");
2782 else
2784 auto f = FormatSpec!dchar("");
2785 f.spec = 'g';
2786 assert(printFloat(real.nan, f) == "nan");
2787 assert(printFloat(-real.nan, f) == "-nan");
2788 assert(printFloat(real.infinity, f) == "inf");
2789 assert(printFloat(-real.infinity, f) == "-inf");
2790 assert(printFloat(0.0L, f) == "0");
2791 assert(printFloat(-0.0L, f) == "-0");
2794 static if (real.mant_dig == 64)
2796 assert(printFloat(1e-4940L, f) == "1e-4940");
2797 assert(printFloat(-1e-4940L, f) == "-1e-4940");
2798 assert(printFloat(1e-30L, f) == "1e-30");
2799 assert(printFloat(-1e-30L, f) == "-1e-30");
2800 assert(printFloat(1e-10L, f) == "1e-10");
2801 assert(printFloat(-1e-10L, f) == "-1e-10");
2802 assert(printFloat(0.1L, f) == "0.1");
2803 assert(printFloat(-0.1L, f) == "-0.1");
2804 assert(printFloat(10.0L, f) == "10");
2805 assert(printFloat(-10.0L, f) == "-10");
2806 version (Windows) {} // issue 20972
2807 else
2809 assert(printFloat(1e4000L, f) == "1e+4000");
2810 assert(printFloat(-1e4000L, f) == "-1e+4000");
2813 import std.math.operations : nextUp, nextDown;
2814 assert(printFloat(nextUp(0.0L), f) == "3.6452e-4951");
2815 assert(printFloat(nextDown(-0.0L), f) == "-3.6452e-4951");
2819 @safe unittest
2821 import std.exception : assertCTFEable;
2822 import std.math.exponential : log2;
2823 import std.math.operations : nextDown;
2825 assertCTFEable!(
2827 // log2 is broken for x87-reals on some computers in CTFE
2828 // the following tests excludes these computers from the tests
2829 // (issue 21757)
2830 enum test = cast(int) log2(3.05e2312L);
2831 static if (real.mant_dig == 64 && test == 7681)
2833 auto f = FormatSpec!dchar("");
2834 f.spec = 'g';
2835 assert(printFloat(real.infinity, f) == "inf");
2836 assert(printFloat(10.0L, f) == "10");
2837 assert(printFloat(2.6080L, f) == "2.608");
2838 assert(printFloat(3.05e2312L, f) == "3.05e+2312");
2840 f.precision = 60;
2841 assert(printFloat(2.65e-54L, f) ==
2842 "2.65000000000000000005900998740054701394102894093529654759941e-54");
2845 commented out, because CTFE is currently too slow for 5000 digits with extreme values
2847 f.precision = 5000;
2848 auto result2 = printFloat(1.2119e-4822L, f);
2849 assert(result2.length == 5007);
2850 assert(result2[$ - 20 .. $] == "26072948659534e-4822");
2851 auto result3 = printFloat(real.min_normal, f);
2852 assert(result3.length == 5007);
2853 assert(result3[$ - 20 .. $] == "72078141008227e-4932");
2854 auto result4 = printFloat(real.min_normal.nextDown, f);
2855 assert(result4.length == 5007);
2856 assert(result4[$ - 20 .. $] == "48141326333101e-4932");
2862 // check no allocations
2863 @safe unittest
2865 import std.format : NoOpSink;
2866 auto w = NoOpSink();
2868 import core.memory;
2869 auto stats = () @trusted { return GC.stats; } ();
2871 auto f = FormatSpec!dchar("");
2872 f.spec = 'a';
2873 printFloat(w, float.nan, f);
2874 printFloat(w, -float.infinity, f);
2875 printFloat(w, 0.0f, f);
2877 printFloat(w, -double.nan, f);
2878 printFloat(w, double.infinity, f);
2879 printFloat(w, -0.0, f);
2881 import std.math.operations : nextUp;
2882 import std.math.constants : E;
2884 printFloat(w, nextUp(0.0f), f);
2885 printFloat(w, cast(float) E, f);
2887 f.precision = 1000;
2888 printFloat(w, float.nan, f);
2889 printFloat(w, 0.0, f);
2890 printFloat(w, 1.23456789e+100, f);
2892 f.spec = 'E';
2893 f.precision = 80;
2894 printFloat(w, 5.62776e+12f, f);
2896 f.precision = 6;
2897 printFloat(w, -1.1418613e+07f, f);
2899 f.precision = 20;
2900 printFloat(w, double.max, f);
2901 printFloat(w, nextUp(0.0), f);
2903 f.precision = 1000;
2904 printFloat(w, 1.0, f);
2906 f.spec = 'f';
2907 f.precision = 15;
2908 printFloat(w, cast(double) E, f);
2910 f.precision = 20;
2911 printFloat(w, double.max, f);
2912 printFloat(w, nextUp(0.0), f);
2914 f.precision = 1000;
2915 printFloat(w, 1.0, f);
2917 f.spec = 'g';
2918 f.precision = 15;
2919 printFloat(w, cast(double) E, f);
2921 f.precision = 20;
2922 printFloat(w, double.max, f);
2923 printFloat(w, nextUp(0.0), f);
2925 f.flHash = true;
2926 f.precision = 1000;
2927 printFloat(w, 1.0, f);
2929 assert(() @trusted { return GC.stats.usedSize; } () == stats.usedSize);