xref: /llvm-project/flang/unittests/Runtime/NumericalFormatTest.cpp (revision c91ba04328e1ded6f284469a7828d181324d4e30)
1 //===-- flang/unittests/Runtime/NumericalFormatTest.cpp ---------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "CrashHandlerFixture.h"
10 #include "flang/Runtime/descriptor.h"
11 #include "flang/Runtime/io-api-consts.h"
12 #include <algorithm>
13 #include <array>
14 #include <cstring>
15 #include <gtest/gtest.h>
16 #include <tuple>
17 
18 using namespace Fortran::runtime;
19 using namespace Fortran::runtime::io;
20 
21 static bool CompareFormattedStrings(
22     const std::string &expect, const std::string &got) {
23   std::string want{expect};
24   want.resize(got.size(), ' ');
25   return want == got;
26 }
27 
28 static bool CompareFormattedStrings(
29     const char *expect, const std::string &&got) {
30   return CompareFormattedStrings(std::string(expect), std::move(got));
31 }
32 
33 // Perform format and compare the result with expected value
34 static bool CompareFormatReal(
35     const char *format, double x, const char *expect, std::string &got) {
36   char buffer[800];
37   auto cookie{IONAME(BeginInternalFormattedOutput)(
38       buffer, sizeof buffer, format, std::strlen(format))};
39   EXPECT_TRUE(IONAME(OutputReal64)(cookie, x));
40   auto status{IONAME(EndIoStatement)(cookie)};
41   EXPECT_EQ(status, 0);
42   got = std::string{buffer, sizeof buffer};
43   auto lastNonBlank{got.find_last_not_of(" ")};
44   if (lastNonBlank != std::string::npos) {
45     got.resize(lastNonBlank + 1);
46   }
47   return CompareFormattedStrings(expect, got);
48 }
49 
50 // Convert raw uint64 into double, perform format, and compare with expected
51 static bool CompareFormatReal(const char *format, std::uint64_t xInt,
52     const char *expect, std::string &got) {
53   double x;
54   static_assert(sizeof(double) == sizeof(std::uint64_t),
55       "Size of double != size of uint64_t!");
56   std::memcpy(&x, &xInt, sizeof xInt);
57   return CompareFormatReal(format, x, expect, got);
58 }
59 
60 static bool CompareFormatInteger(
61     const char *format, std::int64_t x, const char *expect, std::string &got) {
62   char buffer[800];
63   auto cookie{IONAME(BeginInternalFormattedOutput)(
64       buffer, sizeof buffer, format, std::strlen(format))};
65   EXPECT_TRUE(IONAME(OutputInteger64)(cookie, x));
66   auto status{IONAME(EndIoStatement)(cookie)};
67   EXPECT_EQ(status, 0);
68   got = std::string{buffer, sizeof buffer};
69   auto lastNonBlank{got.find_last_not_of(" ")};
70   if (lastNonBlank != std::string::npos) {
71     got.resize(lastNonBlank + 1);
72   }
73   return CompareFormattedStrings(expect, got);
74 }
75 
76 struct IOApiTests : CrashHandlerFixture {};
77 
78 TEST(IOApiTests, HelloWorldOutputTest) {
79   static constexpr int bufferSize{32};
80   char buffer[bufferSize];
81 
82   // Create format for all types and values to be written
83   const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"};
84   auto cookie{IONAME(BeginInternalFormattedOutput)(
85       buffer, bufferSize, format, std::strlen(format))};
86 
87   // Write string, integer, and logical values to buffer
88   IONAME(OutputAscii)(cookie, "WORLD", 5);
89   IONAME(OutputInteger64)(cookie, 678);
90   IONAME(OutputInteger32)(cookie, 0xfeedface);
91   IONAME(OutputLogical)(cookie, true);
92 
93   // Ensure IO succeeded
94   auto status{IONAME(EndIoStatement)(cookie)};
95   ASSERT_EQ(status, 0) << "hello: '" << format << "' failed, status "
96                        << static_cast<int>(status);
97 
98   // Ensure final buffer matches expected string output
99   static const std::string expect{"HELLO, WORLD  678 0xFEEDFACE T"};
100   ASSERT_TRUE(
101       CompareFormattedStrings(expect, std::string{buffer, sizeof buffer}))
102       << "Expected '" << expect << "', got " << buffer;
103 }
104 
105 TEST(IOApiTests, MultilineOutputTest) {
106   // Allocate buffer for multiline output
107   static constexpr int numLines{5};
108   static constexpr int lineLength{32};
109   char buffer[numLines][lineLength];
110 
111   // Create descriptor for entire buffer
112   static constexpr int staticDescriptorMaxRank{1};
113   StaticDescriptor<staticDescriptorMaxRank> wholeStaticDescriptor;
114   Descriptor &whole{wholeStaticDescriptor.descriptor()};
115   static const SubscriptValue extent[]{numLines};
116   whole.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength, &buffer,
117       staticDescriptorMaxRank, extent, CFI_attribute_pointer);
118   whole.Dump(stderr);
119   whole.Check();
120 
121   // Create descriptor for buffer section
122   StaticDescriptor<staticDescriptorMaxRank> sectionStaticDescriptor;
123   Descriptor &section{sectionStaticDescriptor.descriptor()};
124   static const SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1};
125   section.Establish(whole.type(), /*elementBytes=*/whole.ElementBytes(),
126       nullptr, /*maxRank=*/staticDescriptorMaxRank, extent,
127       CFI_attribute_pointer);
128 
129   // Ensure C descriptor address `section.raw()` is updated without error
130   const auto error{
131       CFI_section(&section.raw(), &whole.raw(), lowers, uppers, strides)};
132   ASSERT_EQ(error, 0) << "multiline: CFI_section failed: " << error;
133   section.Dump(stderr);
134   section.Check();
135 
136   // Create format string and initialize IO operation
137   const char *format{
138       "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"};
139   auto cookie{IONAME(BeginInternalArrayFormattedOutput)(
140       section, format, std::strlen(format))};
141 
142   // Fill last line with periods
143   std::memset(buffer[numLines - 1], '.', lineLength);
144 
145   // Write data to buffer
146   IONAME(OutputAscii)(cookie, "WORLD", 5);
147   IONAME(OutputAscii)(cookie, "HELLO", 5);
148   IONAME(OutputInteger64)(cookie, 789);
149   for (int j{666}; j <= 999; j += 111) {
150     IONAME(OutputInteger64)(cookie, j);
151   }
152 
153   // Ensure no errors occured in write operations above
154   const auto status{IONAME(EndIoStatement)(cookie)};
155   ASSERT_EQ(status, 0) << "multiline: '" << format << "' failed, status "
156                        << static_cast<int>(status);
157 
158   static const std::string expect{">HELLO, WORLD                  <"
159                                   "                                "
160                                   "789                 abcd 666 777"
161                                   " 888 999                        "
162                                   "................................"};
163   // Ensure formatted string matches expected output
164   EXPECT_TRUE(
165       CompareFormattedStrings(expect, std::string{buffer[0], sizeof buffer}))
166       << "Expected '" << expect << "' but got '"
167       << std::string{buffer[0], sizeof buffer} << "'";
168 }
169 
170 TEST(IOApiTests, ListInputTest) {
171   static const char input[]{",1*,(5.,6.),(7.0,8.0)"};
172   auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)};
173 
174   // Create real values for IO tests
175   static constexpr int numRealValues{8};
176   float z[numRealValues];
177   for (int j{0}; j < numRealValues; ++j) {
178     z[j] = -(j + 1);
179   }
180 
181   // Ensure reading complex values to floats does not result in an error
182   for (int j{0}; j < numRealValues; j += 2) {
183     ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j]))
184         << "InputComplex32 failed with value " << z[j];
185   }
186 
187   // Ensure no IO errors occured during IO operations above
188   auto status{IONAME(EndIoStatement)(cookie)};
189   ASSERT_EQ(status, 0) << "Failed complex list-directed input, status "
190                        << static_cast<int>(status);
191 
192   // Ensure writing complex values from floats does not result in an error
193   static constexpr int bufferSize{39};
194   char output[bufferSize];
195   output[bufferSize - 1] = '\0';
196   cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1);
197   for (int j{0}; j < numRealValues; j += 2) {
198     ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1]))
199         << "OutputComplex32 failed when outputting value " << z[j] << ", "
200         << z[j + 1];
201   }
202 
203   // Ensure no IO errors occured during IO operations above
204   status = IONAME(EndIoStatement)(cookie);
205   ASSERT_EQ(status, 0) << "Failed complex list-directed output, status "
206                        << static_cast<int>(status);
207 
208   // Verify output buffer against expected value
209   static const char expect[bufferSize]{
210       " (-1.,-2.) (-3.,-4.) (5.,6.) (7.,8.)  "};
211   ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0)
212       << "Failed complex list-directed output, expected '" << expect
213       << "', but got '" << output << "'";
214 }
215 
216 TEST(IOApiTests, DescriptorOutputTest) {
217   static constexpr int bufferSize{10};
218   char buffer[bufferSize];
219   const char *format{"(2A4)"};
220   auto cookie{IONAME(BeginInternalFormattedOutput)(
221       buffer, bufferSize, format, std::strlen(format))};
222 
223   // Create descriptor for output
224   static constexpr int staticDescriptorMaxRank{1};
225   StaticDescriptor<staticDescriptorMaxRank> staticDescriptor;
226   Descriptor &desc{staticDescriptor.descriptor()};
227   static constexpr int subscriptExtent{2};
228   static const SubscriptValue extent[]{subscriptExtent};
229 
230   // Manually write to descriptor buffer
231   static constexpr int dataLength{4};
232   char data[subscriptExtent][dataLength];
233   std::memcpy(data[0], "ABCD", dataLength);
234   std::memcpy(data[1], "EFGH", dataLength);
235   desc.Establish(TypeCode{CFI_type_char}, dataLength, &data,
236       staticDescriptorMaxRank, extent);
237   desc.Dump(stderr);
238   desc.Check();
239   IONAME(OutputDescriptor)(cookie, desc);
240 
241   // Ensure no errors were encountered in initializing the cookie and descriptor
242   auto formatStatus{IONAME(EndIoStatement)(cookie)};
243   ASSERT_EQ(formatStatus, 0)
244       << "descrOutputTest: '" << format << "' failed, status "
245       << static_cast<int>(formatStatus);
246 
247   // Ensure buffer matches expected output
248   EXPECT_TRUE(
249       CompareFormattedStrings("ABCDEFGH  ", std::string{buffer, sizeof buffer}))
250       << "descrOutputTest: formatted: got '"
251       << std::string{buffer, sizeof buffer} << "'";
252 
253   // Begin list-directed output on cookie by descriptor
254   cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer);
255   IONAME(OutputDescriptor)(cookie, desc);
256 
257   // Ensure list-directed output does not result in an IO error
258   auto listDirectedStatus{IONAME(EndIoStatement)(cookie)};
259   ASSERT_EQ(listDirectedStatus, 0)
260       << "descrOutputTest: list-directed failed, status "
261       << static_cast<int>(listDirectedStatus);
262 
263   // Ensure buffer matches expected output
264   EXPECT_TRUE(
265       CompareFormattedStrings(" ABCDEFGH ", std::string{buffer, sizeof buffer}))
266       << "descrOutputTest: list-directed: got '"
267       << std::string{buffer, sizeof buffer} << "'";
268 }
269 
270 //------------------------------------------------------------------------------
271 /// Tests for output formatting real values
272 //------------------------------------------------------------------------------
273 
274 TEST(IOApiTests, FormatZeroes) {
275   static constexpr std::pair<const char *, const char *> zeroes[]{
276       {"(E32.17,';')", "         0.00000000000000000E+00;"},
277       {"(F32.17,';')", "             0.00000000000000000;"},
278       {"(G32.17,';')", "          0.0000000000000000    ;"},
279       {"(DC,E32.17,';')", "         0,00000000000000000E+00;"},
280       {"(DC,F32.17,';')", "             0,00000000000000000;"},
281       {"(DC,G32.17,';')", "          0,0000000000000000    ;"},
282       {"(D32.17,';')", "         0.00000000000000000D+00;"},
283       {"(E32.17E1,';')", "          0.00000000000000000E+0;"},
284       {"(G32.17E1,';')", "           0.0000000000000000   ;"},
285       {"(E32.17E0,';')", "          0.00000000000000000E+0;"},
286       {"(G32.17E0,';')", "          0.0000000000000000    ;"},
287       {"(1P,E32.17,';')", "         0.00000000000000000E+00;"},
288       {"(1PE32.17,';')", "         0.00000000000000000E+00;"}, // no comma
289       {"(1P,F32.17,';')", "             0.00000000000000000;"},
290       {"(1P,G32.17,';')", "          0.0000000000000000    ;"},
291       {"(2P,E32.17,';')", "         00.0000000000000000E+00;"},
292       {"(-1P,E32.17,';')", "         0.00000000000000000E+00;"},
293       {"(EX32.17,';')", "        0X0.00000000000000000P+0;"},
294       {"(DC,EX32.17,';')", "        0X0,00000000000000000P+0;"},
295       {"(G0,';')", "0.;"},
296   };
297 
298   for (auto const &[format, expect] : zeroes) {
299     std::string got;
300     ASSERT_TRUE(CompareFormatReal(format, 0.0, expect, got))
301         << "Failed to format " << format << ", expected '" << expect
302         << "', got '" << got << "'";
303   }
304 }
305 
306 TEST(IOApiTests, FormatOnes) {
307   static constexpr std::pair<const char *, const char *> ones[]{
308       {"(E32.17,';')", "         0.10000000000000000E+01;"},
309       {"(F32.17,';')", "             1.00000000000000000;"},
310       {"(G32.17,';')", "          1.0000000000000000    ;"},
311       {"(E32.17E1,';')", "          0.10000000000000000E+1;"},
312       {"(G32.17E1,';')", "           1.0000000000000000   ;"},
313       {"(E32.17E0,';')", "          0.10000000000000000E+1;"},
314       {"(G32.17E0,';')", "          1.0000000000000000    ;"},
315       {"(E32.17E4,';')", "       0.10000000000000000E+0001;"},
316       {"(G32.17E4,';')", "        1.0000000000000000      ;"},
317       {"(1P,E32.17,';')", "         1.00000000000000000E+00;"},
318       {"(1PE32.17,';')", "         1.00000000000000000E+00;"}, // no comma
319       {"(1P,F32.17,';')", "            10.00000000000000000;"},
320       {"(1P,G32.17,';')", "          1.0000000000000000    ;"},
321       {"(ES32.17,';')", "         1.00000000000000000E+00;"},
322       {"(2P,E32.17,';')", "         10.0000000000000000E-01;"},
323       {"(2P,G32.17,';')", "          1.0000000000000000    ;"},
324       {"(-1P,E32.17,';')", "         0.01000000000000000E+02;"},
325       {"(-1P,G32.17,';')", "          1.0000000000000000    ;"},
326       {"(EX32.17,';')", "        0X8.00000000000000000P-3;"},
327       {"(DC,EX32.17,';')", "        0X8,00000000000000000P-3;"},
328       {"(G0,';')", "1.;"},
329   };
330 
331   for (auto const &[format, expect] : ones) {
332     std::string got;
333     ASSERT_TRUE(CompareFormatReal(format, 1.0, expect, got))
334         << "Failed to format " << format << ", expected '" << expect
335         << "', got '" << got << "'";
336   }
337 }
338 
339 TEST(IOApiTests, FormatNegativeOnes) {
340   static constexpr std::tuple<const char *, const char *> negOnes[]{
341       {"(E32.17,';')", "        -0.10000000000000000E+01;"},
342       {"(F32.17,';')", "            -1.00000000000000000;"},
343       {"(G32.17,';')", "         -1.0000000000000000    ;"},
344       {"(EX32.17,';')", "       -0X8.00000000000000000P-3;"},
345       {"(G0,';')", "-1.;"},
346   };
347   for (auto const &[format, expect] : negOnes) {
348     std::string got;
349     ASSERT_TRUE(CompareFormatReal(format, -1.0, expect, got))
350         << "Failed to format " << format << ", expected '" << expect
351         << "', got '" << got << "'";
352   }
353 }
354 
355 // Each test case contains a raw uint64, a format string for a real value, and
356 // the expected resulting string from formatting the raw uint64. The double
357 // representation of the uint64 is commented above each test case.
358 TEST(IOApiTests, FormatDoubleValues) {
359 
360   using TestCaseTy = std::tuple<std::uint64_t,
361       std::vector<std::tuple<const char *, const char *>>>;
362   static const std::vector<TestCaseTy> testCases{
363       {// -0
364           0x8000000000000000,
365           {
366               {"(E9.1,';')", " -0.0E+00;"},
367               {"(F4.0,';')", " -0.;"},
368               {"(F0.1,';')", "-.0;"},
369               {"(G8.0,';')", "-0.0E+00;"},
370               {"(G8.1,';')", " -0.    ;"},
371               {"(G0,';')", "-0.;"},
372               {"(E9.1,';')", " -0.0E+00;"},
373               {"(EX9.1,';')", "-0X0.0P+0;"},
374           }},
375       {// +Inf
376           0x7ff0000000000000,
377           {
378               {"(E9.1,';')", "      Inf;"},
379               {"(F9.1,';')", "      Inf;"},
380               {"(G9.1,';')", "      Inf;"},
381               {"(EX9.1,';')", "      Inf;"},
382               {"(SP,E9.1,';')", "     +Inf;"},
383               {"(SP,F9.1,';')", "     +Inf;"},
384               {"(SP,G9.1,';')", "     +Inf;"},
385               {"(SP,EX9.1,';')", "     +Inf;"},
386               {"(G0,';')", "Inf;"},
387           }},
388       {// -Inf
389           0xfff0000000000000,
390           {
391               {"(E9.1,';')", "     -Inf;"},
392               {"(F9.1,';')", "     -Inf;"},
393               {"(G9.1,';')", "     -Inf;"},
394               {"(EX9.1,';')", "     -Inf;"},
395               {"(G0,';')", "-Inf;"},
396           }},
397       {// NaN
398           0x7ff0000000000001,
399           {
400               {"(E9.1,';')", "      NaN;"},
401               {"(F9.1,';')", "      NaN;"},
402               {"(G9.1,';')", "      NaN;"},
403               {"(EX9.1,';')", "      NaN;"},
404               {"(G0,';')", "NaN;"},
405           }},
406       {// NaN (sign irrelevant)
407           0xfff0000000000001,
408           {
409               {"(E9.1,';')", "      NaN;"},
410               {"(F9.1,';')", "      NaN;"},
411               {"(G9.1,';')", "      NaN;"},
412               {"(SP,E9.1,';')", "      NaN;"},
413               {"(SP,F9.1,';')", "      NaN;"},
414               {"(SP,G9.1,';')", "      NaN;"},
415               {"(SP,EX9.1,';')", "      NaN;"},
416               {"(G0,';')", "NaN;"},
417           }},
418       {// 0.1 rounded
419           0x3fb999999999999a,
420           {
421               {"(E62.55,';')",
422                   " 0.1000000000000000055511151231257827021181583404541015625E+"
423                   "00;"},
424               {"(E0.1,';')", ".1E+00;"},
425               {"(E0.55,';')",
426                   ".1000000000000000055511151231257827021181583404541015625E+"
427                   "00;"},
428               {"(E0,';')", ".1E+00;"},
429               {"(F58.55,';')",
430                   " 0."
431                   "1000000000000000055511151231257827021181583404541015625;"},
432               {"(F0.0,';')", "0.;"},
433               {"(F0.55,';')",
434                   ".1000000000000000055511151231257827021181583404541015625;"},
435               {"(F0,';')", ".1;"},
436               {"(G62.55,';')",
437                   " 0.1000000000000000055511151231257827021181583404541015625  "
438                   "  ;"},
439               {"(G0.0,';')", "0.;"},
440               {"(G0.55,';')",
441                   ".1000000000000000055511151231257827021181583404541015625;"},
442               {"(G0,';')", ".1;"},
443               {"(EX20.12,';')", " 0XC.CCCCCCCCCCCDP-7;"},
444           }},
445       {// 1.5
446           0x3ff8000000000000,
447           {
448               {"(E9.2,';')", " 0.15E+01;"},
449               {"(F4.1,';')", " 1.5;"},
450               {"(G7.1,';')", " 2.    ;"},
451               {"(EX9.1,';')", " 0XC.0P-3;"},
452               {"(RN,E8.1,';')", " 0.2E+01;"},
453               {"(RN,F3.0,';')", " 2.;"},
454               {"(RN,G7.0,';')", " 0.E+01;"},
455               {"(RN,G7.1,';')", " 2.    ;"},
456               {"(RD,E8.1,';')", " 0.1E+01;"},
457               {"(RD,F3.0,';')", " 1.;"},
458               {"(RD,G7.0,';')", " 0.E+01;"},
459               {"(RD,G7.1,';')", " 1.    ;"},
460               {"(RU,E8.1,';')", " 0.2E+01;"},
461               {"(RU,G7.0,';')", " 0.E+01;"},
462               {"(RU,G7.1,';')", " 2.    ;"},
463               {"(RZ,E8.1,';')", " 0.1E+01;"},
464               {"(RZ,F3.0,';')", " 1.;"},
465               {"(RZ,G7.0,';')", " 0.E+01;"},
466               {"(RZ,G7.1,';')", " 1.    ;"},
467               {"(RC,E8.1,';')", " 0.2E+01;"},
468               {"(RC,F3.0,';')", " 2.;"},
469               {"(RC,G7.0,';')", " 0.E+01;"},
470               {"(RC,G7.1,';')", " 2.    ;"},
471           }},
472       {// -1.5
473           0xbff8000000000000,
474           {
475               {"(E9.2,';')", "-0.15E+01;"},
476               {"(RN,E8.1,';')", "-0.2E+01;"},
477               {"(RD,E8.1,';')", "-0.2E+01;"},
478               {"(RU,E8.1,';')", "-0.1E+01;"},
479               {"(RZ,E8.1,';')", "-0.1E+01;"},
480               {"(RC,E8.1,';')", "-0.2E+01;"},
481               {"(EX9.1,';')", "-0XC.0P-3;"},
482           }},
483       {// 2.5
484           0x4004000000000000,
485           {
486               {"(E9.2,';')", " 0.25E+01;"},
487               {"(RN,E8.1,';')", " 0.2E+01;"},
488               {"(RD,E8.1,';')", " 0.2E+01;"},
489               {"(RU,E8.1,';')", " 0.3E+01;"},
490               {"(RZ,E8.1,';')", " 0.2E+01;"},
491               {"(RC,E8.1,';')", " 0.3E+01;"},
492               {"(EX9.1,';')", " 0XA.0P-2;"},
493           }},
494       {// -2.5
495           0xc004000000000000,
496           {
497               {"(E9.2,';')", "-0.25E+01;"},
498               {"(RN,E8.1,';')", "-0.2E+01;"},
499               {"(RD,E8.1,';')", "-0.3E+01;"},
500               {"(RU,E8.1,';')", "-0.2E+01;"},
501               {"(RZ,E8.1,';')", "-0.2E+01;"},
502               {"(RC,E8.1,';')", "-0.3E+01;"},
503               {"(EX9.1,';')", "-0XA.0P-2;"},
504           }},
505       {// least positive nonzero subnormal
506           1,
507           {
508               {"(E32.17,';')", "         0.49406564584124654-323;"},
509               {"(ES32.17,';')", "         4.94065645841246544-324;"},
510               {"(EN32.17,';')", "         4.94065645841246544-324;"},
511               {"(E759.752,';')",
512                   " 0."
513                   "494065645841246544176568792868221372365059802614324764425585"
514                   "682500675507270208751865299836361635992379796564695445717730"
515                   "926656710355939796398774796010781878126300713190311404527845"
516                   "817167848982103688718636056998730723050006387409153564984387"
517                   "312473397273169615140031715385398074126238565591171026658556"
518                   "686768187039560310624931945271591492455329305456544401127480"
519                   "129709999541931989409080416563324524757147869014726780159355"
520                   "238611550134803526493472019379026810710749170333222684475333"
521                   "572083243193609238289345836806010601150616980975307834227731"
522                   "832924790498252473077637592724787465608477820373446969953364"
523                   "701797267771758512566055119913150489110145103786273816725095"
524                   "583738973359899366480994116420570263709027924276754456522908"
525                   "75386825064197182655334472656250-323;"},
526               {"(G0,';')", ".5E-323;"},
527               {"(E757.750,';')",
528                   " 0."
529                   "494065645841246544176568792868221372365059802614324764425585"
530                   "682500675507270208751865299836361635992379796564695445717730"
531                   "926656710355939796398774796010781878126300713190311404527845"
532                   "817167848982103688718636056998730723050006387409153564984387"
533                   "312473397273169615140031715385398074126238565591171026658556"
534                   "686768187039560310624931945271591492455329305456544401127480"
535                   "129709999541931989409080416563324524757147869014726780159355"
536                   "238611550134803526493472019379026810710749170333222684475333"
537                   "572083243193609238289345836806010601150616980975307834227731"
538                   "832924790498252473077637592724787465608477820373446969953364"
539                   "701797267771758512566055119913150489110145103786273816725095"
540                   "583738973359899366480994116420570263709027924276754456522908"
541                   "753868250641971826553344726562-323;"},
542               {"(RN,E757.750,';')",
543                   " 0."
544                   "494065645841246544176568792868221372365059802614324764425585"
545                   "682500675507270208751865299836361635992379796564695445717730"
546                   "926656710355939796398774796010781878126300713190311404527845"
547                   "817167848982103688718636056998730723050006387409153564984387"
548                   "312473397273169615140031715385398074126238565591171026658556"
549                   "686768187039560310624931945271591492455329305456544401127480"
550                   "129709999541931989409080416563324524757147869014726780159355"
551                   "238611550134803526493472019379026810710749170333222684475333"
552                   "572083243193609238289345836806010601150616980975307834227731"
553                   "832924790498252473077637592724787465608477820373446969953364"
554                   "701797267771758512566055119913150489110145103786273816725095"
555                   "583738973359899366480994116420570263709027924276754456522908"
556                   "753868250641971826553344726562-323;"},
557               {"(RD,E757.750,';')",
558                   " 0."
559                   "494065645841246544176568792868221372365059802614324764425585"
560                   "682500675507270208751865299836361635992379796564695445717730"
561                   "926656710355939796398774796010781878126300713190311404527845"
562                   "817167848982103688718636056998730723050006387409153564984387"
563                   "312473397273169615140031715385398074126238565591171026658556"
564                   "686768187039560310624931945271591492455329305456544401127480"
565                   "129709999541931989409080416563324524757147869014726780159355"
566                   "238611550134803526493472019379026810710749170333222684475333"
567                   "572083243193609238289345836806010601150616980975307834227731"
568                   "832924790498252473077637592724787465608477820373446969953364"
569                   "701797267771758512566055119913150489110145103786273816725095"
570                   "583738973359899366480994116420570263709027924276754456522908"
571                   "753868250641971826553344726562-323;"},
572               {"(RU,E757.750,';')",
573                   " 0."
574                   "494065645841246544176568792868221372365059802614324764425585"
575                   "682500675507270208751865299836361635992379796564695445717730"
576                   "926656710355939796398774796010781878126300713190311404527845"
577                   "817167848982103688718636056998730723050006387409153564984387"
578                   "312473397273169615140031715385398074126238565591171026658556"
579                   "686768187039560310624931945271591492455329305456544401127480"
580                   "129709999541931989409080416563324524757147869014726780159355"
581                   "238611550134803526493472019379026810710749170333222684475333"
582                   "572083243193609238289345836806010601150616980975307834227731"
583                   "832924790498252473077637592724787465608477820373446969953364"
584                   "701797267771758512566055119913150489110145103786273816725095"
585                   "583738973359899366480994116420570263709027924276754456522908"
586                   "753868250641971826553344726563-323;"},
587               {"(RC,E757.750,';')",
588                   " 0."
589                   "494065645841246544176568792868221372365059802614324764425585"
590                   "682500675507270208751865299836361635992379796564695445717730"
591                   "926656710355939796398774796010781878126300713190311404527845"
592                   "817167848982103688718636056998730723050006387409153564984387"
593                   "312473397273169615140031715385398074126238565591171026658556"
594                   "686768187039560310624931945271591492455329305456544401127480"
595                   "129709999541931989409080416563324524757147869014726780159355"
596                   "238611550134803526493472019379026810710749170333222684475333"
597                   "572083243193609238289345836806010601150616980975307834227731"
598                   "832924790498252473077637592724787465608477820373446969953364"
599                   "701797267771758512566055119913150489110145103786273816725095"
600                   "583738973359899366480994116420570263709027924276754456522908"
601                   "753868250641971826553344726563-323;"},
602               {"(EX24.13,';')", " 0X8.0000000000000P-1077;"},
603           }},
604       {// least positive nonzero normal
605           0x10000000000000,
606           {
607               {"(E723.716,';')",
608                   " 0."
609                   "222507385850720138309023271733240406421921598046233183055332"
610                   "741688720443481391819585428315901251102056406733973103581100"
611                   "515243416155346010885601238537771882113077799353200233047961"
612                   "014744258363607192156504694250373420837525080665061665815894"
613                   "872049117996859163964850063590877011830487479978088775374994"
614                   "945158045160505091539985658247081864511353793580499211598108"
615                   "576605199243335211435239014879569960959128889160299264151106"
616                   "346631339366347758651302937176204732563178148566435087212282"
617                   "863764204484681140761391147706280168985324411002416144742161"
618                   "856716615054015428508471675290190316132277889672970737312333"
619                   "408698898317506783884692609277397797285865965494109136909540"
620                   "61364675687023986783152906809846172109246253967285156250-"
621                   "307;"},
622               {"(G0,';')", ".22250738585072014E-307;"},
623               {"(EX24.13,';')", " 0X8.0000000000000P-1025;"},
624           }},
625       {// greatest finite
626           0x7fefffffffffffffuLL,
627           {
628               {"(E32.17,';')", "         0.17976931348623157+309;"},
629               {"(E317.310,';')",
630                   " 0."
631                   "179769313486231570814527423731704356798070567525844996598917"
632                   "476803157260780028538760589558632766878171540458953514382464"
633                   "234321326889464182768467546703537516986049910576551282076245"
634                   "490090389328944075868508455133942304583236903222948165808559"
635                   "332123348274797826204144723168738177180919299881250404026184"
636                   "1248583680+309;"},
637               {"(ES317.310,';')",
638                   " 1."
639                   "797693134862315708145274237317043567980705675258449965989174"
640                   "768031572607800285387605895586327668781715404589535143824642"
641                   "343213268894641827684675467035375169860499105765512820762454"
642                   "900903893289440758685084551339423045832369032229481658085593"
643                   "321233482747978262041447231687381771809192998812504040261841"
644                   "2485836800+308;"},
645               {"(EN319.310,';')",
646                   " 179."
647                   "769313486231570814527423731704356798070567525844996598917476"
648                   "803157260780028538760589558632766878171540458953514382464234"
649                   "321326889464182768467546703537516986049910576551282076245490"
650                   "090389328944075868508455133942304583236903222948165808559332"
651                   "123348274797826204144723168738177180919299881250404026184124"
652                   "8583680000+306;"},
653               {"(G0,';')", ".17976931348623157E+309;"},
654               {"(EX24.13,';')", " 0XF.FFFFFFFFFFFF8P+1020;"},
655           }},
656       {// EX rounding
657           0x3ff0100000000000uLL,
658           {
659               {"(F11.8,';')", " 1.00390625;"},
660               {"(EX10.2,';')", " 0X8.08P-3;"},
661               {"(EX10.1,';')", "  0X8.0P-3;"},
662               {"(EX10.0,';')", " 0X8.08P-3;"},
663               {"(EX0.0,';')", "0X8.08P-3;"},
664               {"(EX0,';')", "0X8.08P-3;"},
665               {"(RN,EX10.1,';')", "  0X8.0P-3;"},
666               {"(RU,EX10.1,';')", "  0X8.1P-3;"},
667               {"(RD,EX10.1,';')", "  0X8.0P-3;"},
668               {"(RZ,EX10.1,';')", "  0X8.0P-3;"},
669               {"(RC,EX10.1,';')", "  0X8.1P-3;"},
670               {"(RN,EX10.0,';')", " 0X8.08P-3;"},
671               {"(RU,EX10.0,';')", " 0X8.08P-3;"},
672               {"(RD,EX10.0,';')", " 0X8.08P-3;"},
673               {"(RZ,EX10.0,';')", " 0X8.08P-3;"},
674               {"(RC,EX10.0,';')", " 0X8.08P-3;"},
675           }},
676       {// EX rounding
677           0xbff0100000000000uLL,
678           {
679               {"(F11.8,';')", "-1.00390625;"},
680               {"(EX10.2,';')", "-0X8.08P-3;"},
681               {"(EX10.1,';')", " -0X8.0P-3;"},
682               {"(EX10.0,';')", "-0X8.08P-3;"},
683               {"(EX0.0,';')", "-0X8.08P-3;"},
684               {"(EX0,';')", "-0X8.08P-3;"},
685               {"(RN,EX10.1,';')", " -0X8.0P-3;"},
686               {"(RU,EX10.1,';')", " -0X8.0P-3;"},
687               {"(RD,EX10.1,';')", " -0X8.1P-3;"},
688               {"(RZ,EX10.1,';')", " -0X8.0P-3;"},
689               {"(RC,EX10.1,';')", " -0X8.1P-3;"},
690               {"(RN,EX10.0,';')", "-0X8.08P-3;"},
691               {"(RU,EX10.0,';')", "-0X8.08P-3;"},
692               {"(RD,EX10.0,';')", "-0X8.08P-3;"},
693               {"(RZ,EX10.0,';')", "-0X8.08P-3;"},
694               {"(RC,EX10.0,';')", "-0X8.08P-3;"},
695           }},
696   };
697 
698   for (auto const &[value, cases] : testCases) {
699     for (auto const &[format, expect] : cases) {
700       std::string got;
701       ASSERT_TRUE(CompareFormatReal(format, value, expect, got))
702           << "Failed to format " << format << ", expected '" << expect
703           << "', got '" << got << "'";
704     }
705   }
706 
707   using IndividualTestCaseTy = std::tuple<const char *, double, const char *>;
708   static const std::vector<IndividualTestCaseTy> individualTestCases{
709       {"(F5.3,';')", 25., "*****;"},
710       {"(F5.3,';')", 2.5, "2.500;"},
711       {"(F5.3,';')", 0.25, "0.250;"},
712       {"(F5.3,';')", 0.025, "0.025;"},
713       {"(F5.3,';')", 0.0025, "0.003;"},
714       {"(F5.3,';')", 0.00025, "0.000;"},
715       {"(F5.3,';')", 0.000025, "0.000;"},
716       {"(F5.3,';')", -25., "*****;"},
717       {"(F5.3,';')", -2.5, "*****;"},
718       {"(F5.3,';')", -0.25, "-.250;"},
719       {"(F5.3,';')", -0.025, "-.025;"},
720       {"(F5.3,';')", -0.0025, "-.003;"},
721       {"(F5.3,';')", -0.00025, "-.000;"},
722       {"(F5.3,';')", -0.000025, "-.000;"},
723       {"(F5.3,';')", 99.999, "*****;"},
724       {"(F5.3,';')", 9.9999, "*****;"},
725       {"(F5.3,';')", 0.99999, "1.000;"},
726       {"(F5.3,';')", 0.099999, "0.100;"},
727       {"(F5.3,';')", 0.0099999, "0.010;"},
728       {"(F5.3,';')", 0.00099999, "0.001;"},
729       {"(F5.3,';')",
730           0.0005000000000000000104083408558608425664715468883514404296875,
731           "0.001;"},
732       {"(F5.3,';')",
733           0.000499999999999999901988123607310399165726266801357269287109375,
734           "0.000;"},
735       {"(F5.3,';')", 0.000099999, "0.000;"},
736       {"(F5.3,';')", -99.999, "*****;"},
737       {"(F5.3,';')", -9.9999, "*****;"},
738       {"(F5.3,';')", -0.99999, "*****;"},
739       {"(F5.3,';')", -0.099999, "-.100;"},
740       {"(F5.3,';')", -0.0099999, "-.010;"},
741       {"(F5.3,';')", -0.00099999, "-.001;"},
742       {"(F5.3,';')",
743           -0.0005000000000000000104083408558608425664715468883514404296875,
744           "-.001;"},
745       {"(F5.3,';')",
746           -0.000499999999999999901988123607310399165726266801357269287109375,
747           "-.000;"},
748       {"(F5.3,';')", -0.000099999, "-.000;"},
749       {"(F0.1,';')", 0.0, ".0;"},
750       {"(F5.0,';')", -0.5000000000000001, "  -1.;"},
751       {"(F5.0,';')", -0.5, "  -0.;"},
752       {"(F5.0,';')", -0.49999999999999994, "  -0.;"},
753       {"(F5.0,';')", 0.49999999999999994, "   0.;"},
754       {"(F5.0,';')", 0.5, "   0.;"},
755       {"(F5.0,';')", 0.5000000000000001, "   1.;"},
756       {"(ES0.0E0,';')", 0.666, "7.E-1;"},
757       {"(EN0.0E0,';')", 0.666, "666.E-3;"},
758   };
759 
760   for (auto const &[format, value, expect] : individualTestCases) {
761     std::string got;
762     char hex[17];
763     std::snprintf(hex, sizeof hex, "%016llx",
764         *reinterpret_cast<const unsigned long long *>(&value));
765     ASSERT_TRUE(CompareFormatReal(format, value, expect, got))
766         << "Failed to format " << value << " 0x" << hex << " with format "
767         << format << ", expected '" << expect << "', got '" << got << "'";
768   }
769 
770   // Problematic EN formatting edge cases with rounding
771   using IndividualENTestCaseTy = std::tuple<std::uint64_t, const char *>;
772   static const std::vector<IndividualENTestCaseTy> individualENTestCases{
773       {0x3E11183197785F8C, " 995.0E-12"}, // 0.9950312500000000115852E-09
774       {0x3E11180E68455D30, " 995.0E-12"}, // 0.9949999999999999761502E-09
775       {0x3E112BD8F4F6B0D7, " 999.5E-12"}, // 0.9994999999999999089118E-09
776       {0x3E45794883CA8782, "  10.0E-09"}, // 0.9999499999999999642266E-08
777       {0x3F506218230C7482, " 999.9E-06"}, // 0.9999499999999998840761E-03
778       {0x3FB99652BD3C3612, " 100.0E-03"}, // 0.9999500000000000055067E+00
779       {0x4023E66666666667, "  10.0E+00"}, // 0.9950000000000001065814E+01
780   };
781 
782   for (auto const &[value, expect] : individualENTestCases) {
783     std::string got;
784     ASSERT_TRUE(CompareFormatReal("(EN10.1)", value, expect, got))
785         << "Failed to format EN10.1, expected '" << expect << "', got '" << got
786         << "'";
787   }
788 }
789 
790 TEST(IOApiTests, FormatIntegerValues) {
791   using IntTestCaseTy = std::tuple<const char *, std::int64_t, const char *>;
792   static const std::vector<IntTestCaseTy> intTestCases{
793       {"(I4)", 0, "   0"},
794       {"(I4)", 1, "   1"},
795       {"(I4)", 9999, "9999"},
796       {"(SP,I4)", 1, "  +1"},
797       {"(SP,I4)", 9999, "****"},
798       {"(SP,I4)", 999, "+999"},
799       {"(I4)", -1, "  -1"},
800       {"(I4)", -9999, "****"},
801       {"(I4)", -999, "-999"},
802       {"(I4.2)", 1, "  01"},
803       {"(I4.2)", -1, " -01"},
804       {"(I4.2)", 999, " 999"},
805       {"(I4.4)", 999, "0999"},
806       {"(I0)", 0, "0"},
807       {"(I0)", 1, "1"},
808       {"(I0)", 9999, "9999"},
809       {"(SP,I0)", 1, "+1"},
810       {"(SP,I0)", 9999, "+9999"},
811       {"(SP,I0)", 999, "+999"},
812       {"(I0)", -1, "-1"},
813       {"(I0)", -9999, "-9999"},
814       {"(I0)", -999, "-999"},
815       {"(I0.2)", 1, "01"},
816       {"(I0.2)", -1, "-01"},
817       {"(I0.2)", 999, "999"},
818       {"(I0.4)", 999, "0999"},
819       {"(G4)", 0, "   0"},
820       {"(G4)", 1, "   1"},
821       {"(G4)", 9999, "9999"},
822       {"(SP,G4)", 1, "  +1"},
823       {"(SP,G4)", 9999, "****"},
824       {"(SP,G4)", 999, "+999"},
825       {"(G4)", -1, "  -1"},
826       {"(G4)", -9999, "****"},
827       {"(G4)", -999, "-999"},
828       {"(G4.2)", 1, "   1"},
829       {"(G4.2)", -1, "  -1"},
830       {"(G4.2)", 999, " 999"},
831       {"(G4.4)", 999, " 999"},
832       {"(G0)", 0, "0"},
833       {"(G0)", 1, "1"},
834       {"(G0)", 9999, "9999"},
835       {"(SP,G0)", 1, "+1"},
836       {"(SP,G0)", 9999, "+9999"},
837       {"(SP,G0)", 999, "+999"},
838       {"(G0)", -1, "-1"},
839       {"(G0)", -9999, "-9999"},
840       {"(G0)", -999, "-999"},
841       {"(G0.2)", 1, "1"},
842       {"(G0.2)", -1, "-1"},
843       {"(G0.2)", 999, "999"},
844       {"(G0.4)", 999, "999"},
845   };
846 
847   for (auto const &[fmt, value, expect] : intTestCases) {
848     std::string got;
849     ASSERT_TRUE(CompareFormatInteger(fmt, value, expect, got))
850         << "Failed to format " << fmt << ", expected '" << expect << "', got '"
851         << got << "'";
852   }
853 }
854 
855 //------------------------------------------------------------------------------
856 /// Tests for input editing real values
857 //------------------------------------------------------------------------------
858 
859 // Ensure double input values correctly map to raw uint64 values
860 TEST(IOApiTests, EditDoubleInputValues) {
861   using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t, int>;
862   int ovf{IostatRealInputOverflow};
863   static const std::vector<TestCaseTy> testCases{
864       {"(F18.0)", "                 0", 0x0, 0},
865       {"(F18.0)", "                  ", 0x0, 0},
866       {"(F18.0)", "                -0", 0x8000000000000000, 0},
867       {"(F18.0)", "                01", 0x3ff0000000000000, 0},
868       {"(F18.0)", "                 1", 0x3ff0000000000000, 0},
869       {"(F18.0)", "              125.", 0x405f400000000000, 0},
870       {"(F18.0)", "              12.5", 0x4029000000000000, 0},
871       {"(F18.0)", "              1.25", 0x3ff4000000000000, 0},
872       {"(F18.0)", "             01.25", 0x3ff4000000000000, 0},
873       {"(F18.0)", "              .125", 0x3fc0000000000000, 0},
874       {"(F18.0)", "             0.125", 0x3fc0000000000000, 0},
875       {"(F18.0)", "             .0625", 0x3fb0000000000000, 0},
876       {"(F18.0)", "            0.0625", 0x3fb0000000000000, 0},
877       {"(F18.0)", "               125", 0x405f400000000000, 0},
878       {"(F18.1)", "               125", 0x4029000000000000, 0},
879       {"(F18.2)", "               125", 0x3ff4000000000000, 0},
880       {"(F18.3)", "               125", 0x3fc0000000000000, 0},
881       {"(-1P,F18.0)", "               125", 0x4093880000000000, 0}, // 1250
882       {"(1P,F18.0)", "               125", 0x4029000000000000, 0}, // 12.5
883       {"(BZ,F18.0)", "              125 ", 0x4093880000000000, 0}, // 1250
884       {"(BZ,F18.0)", "       125 . e +1 ", 0x42a6bcc41e900000, 0}, // 1.25e13
885       {"(BZ,F18.0)", "           .      ", 0x0, 0},
886       {"(BZ,F18.0)", "           . e +1 ", 0x0, 0},
887       {"(DC,F18.0)", "              12,5", 0x4029000000000000, 0},
888       {"(EX22.0)", "0X0P0                 ", 0x0, 0}, // +0.
889       {"(EX22.0)", "-0X0P0                ", 0x8000000000000000, 0}, // -0.
890       {"(EX22.0)", "0X.8P1                ", 0x3ff0000000000000, 0}, // 1.0
891       {"(EX22.0)", "0X8.P-3               ", 0x3ff0000000000000, 0}, // 1.0
892       {"(EX22.0)", "0X.1P4                ", 0x3ff0000000000000, 0}, // 1.0
893       {"(EX22.0)", "0X10.P-4              ", 0x3ff0000000000000, 0}, // 1.0
894       {"(EX22.0)", "0X8.00P-3             ", 0x3ff0000000000000, 0}, // 1.0
895       {"(EX22.0)", "0X80.0P-6             ", 0x4000000000000000, 0}, // 2.0
896       {"(EX22.0)", "0XC.CCCCCCCCCCCDP-7   ", 0x3fb999999999999a, 0}, // 0.1
897       {"(EX22.0)", "0X.8P-1021            ", 0x0010000000000000,
898           0}, // min normal
899       {"(EX22.0)", "0X.8P-1022            ", 0x0008000000000000,
900           0}, // subnormal
901       {"(EX22.0)", "0X.8P-1073            ", 0x0000000000000001,
902           0}, // min subn.
903       {"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff,
904           0}, // max finite
905       {"(EX22.0)", "0X.8P1025             ", 0x7ff0000000000000, ovf}, // +Inf
906       {"(EX22.0)", "-0X.8P1025            ", 0xfff0000000000000, ovf}, // -Inf
907       {"(RC,EX22.0)", "0X1.0000000000000P0   ", 0x3ff0000000000000, 0},
908       {"(RC,EX22.0)", "0X1.00000000000008P0  ", 0x3ff0000000000001, 0},
909       {"(RC,EX22.0)", "0X1.000000000000008P0 ", 0x3ff0000000000000, 0},
910       {"(RC,EX22.0)", "0X1.00000000000004P0  ", 0x3ff0000000000000, 0},
911       {"(RC,EX22.0)", "0X.80000000000000P1   ", 0x3ff0000000000000, 0},
912       {"(RC,EX22.0)", "0X.80000000000004P1   ", 0x3ff0000000000001, 0},
913       {"(RC,EX22.0)", "0X.800000000000004P1  ", 0x3ff0000000000000, 0},
914       {"(RC,EX22.0)", "0X.80000000000002P1   ", 0x3ff0000000000000, 0},
915       {"(RZ,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE()
916       {"(RD,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE()
917       {"(RU,F7.0)", " 2.e308", 0x7ff0000000000000, ovf}, // +Inf
918       {"(RZ,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE()
919       {"(RD,F7.0)", "-2.e308", 0xfff0000000000000, ovf}, // -Inf
920       {"(RU,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE()
921       {"(RZ,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE()
922       {"(RD,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE()
923       {"(RU,F7.0)", " 1.e999", 0x7ff0000000000000, ovf}, // +Inf
924       {"(RZ,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE()
925       {"(RD,F7.0)", "-1.e999", 0xfff0000000000000, ovf}, // -Inf
926       {"(RU,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE()
927       {"(E9.1)", " 1.0E-325", 0x0, 0},
928       {"(RU,E9.1)", " 1.0E-325", 0x1, 0},
929       {"(E9.1)", "-1.0E-325", 0x8000000000000000, 0},
930       {"(RD,E9.1)", "-1.0E-325", 0x8000000000000001, 0},
931   };
932   for (auto const &[format, data, want, iostat] : testCases) {
933     auto cookie{IONAME(BeginInternalFormattedInput)(
934         data, std::strlen(data), format, std::strlen(format))};
935     union {
936       double x;
937       std::uint64_t raw;
938     } u;
939     u.raw = 0;
940 
941     // Read buffer into union value
942     IONAME(EnableHandlers)(cookie, true, true, true, true, true);
943     IONAME(InputReal64)(cookie, u.x);
944 
945     static constexpr int bufferSize{65};
946     char iomsg[bufferSize];
947     std::memset(iomsg, '\0', bufferSize - 1);
948 
949     // Ensure no unexpected errors were encountered reading input buffer into
950     // union value
951     IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1);
952     auto status{IONAME(EndIoStatement)(cookie)};
953     ASSERT_EQ(status, iostat)
954         << '\'' << format << "' failed reading '" << data << "', status "
955         << static_cast<int>(status) << " != expected " << iostat << " iomsg '"
956         << iomsg << "'";
957 
958     // Ensure raw uint64 value matches expected conversion from double
959     ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data
960                            << "', want " << want << ", got " << u.raw;
961   }
962 }
963 
964 // regression test for confusing digit minimization
965 TEST(IOApiTests, ConfusingMinimization) {
966   char buffer[8]{};
967   auto cookie{IONAME(BeginInternalListOutput)(buffer, sizeof buffer)};
968   StaticDescriptor<0> staticDescriptor;
969   Descriptor &desc{staticDescriptor.descriptor()};
970   std::uint16_t x{0x7bff}; // HUGE(0._2)
971   desc.Establish(TypeCode{CFI_type_half_float}, sizeof x, &x, 0, nullptr);
972   desc.Check();
973   EXPECT_TRUE(IONAME(OutputDescriptor)(cookie, desc));
974   auto status{IONAME(EndIoStatement)(cookie)};
975   EXPECT_EQ(status, 0);
976   std::string got{std::string{buffer, sizeof buffer}};
977   EXPECT_TRUE(CompareFormattedStrings(" 65504. ", got))
978       << "expected ' 65504. ', got '" << got << '\''; // not 65500.!
979 }
980