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.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) {
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   return CompareFormattedStrings(expect, std::string{buffer, sizeof buffer});
43 }
44 
45 // Convert raw uint64 into double, perform format, and compare with expected
46 static bool CompareFormatReal(
47     const char *format, std::uint64_t xInt, const char *expect) {
48   double x;
49   static_assert(sizeof(double) == sizeof(std::uint64_t),
50       "Size of double != size of uint64_t!");
51   std::memcpy(&x, &xInt, sizeof xInt);
52   return CompareFormatReal(format, x, expect);
53 }
54 
55 struct IOApiTests : CrashHandlerFixture {};
56 
57 TEST(IOApiTests, HelloWorldOutputTest) {
58   static constexpr int bufferSize{32};
59   char buffer[bufferSize];
60 
61   // Create format for all types and values to be written
62   const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"};
63   auto cookie{IONAME(BeginInternalFormattedOutput)(
64       buffer, bufferSize, format, std::strlen(format))};
65 
66   // Write string, integer, and logical values to buffer
67   IONAME(OutputAscii)(cookie, "WORLD", 5);
68   IONAME(OutputInteger64)(cookie, 678);
69   IONAME(OutputInteger32)(cookie, 0xfeedface);
70   IONAME(OutputLogical)(cookie, true);
71 
72   // Ensure IO succeeded
73   auto status{IONAME(EndIoStatement)(cookie)};
74   ASSERT_EQ(status, 0) << "hello: '" << format << "' failed, status "
75                        << static_cast<int>(status);
76 
77   // Ensure final buffer matches expected string output
78   static const std::string expect{"HELLO, WORLD  678 0xFEEDFACE T"};
79   ASSERT_TRUE(
80       CompareFormattedStrings(expect, std::string{buffer, sizeof buffer}))
81       << "Expected '" << expect << "', got " << buffer;
82 }
83 
84 TEST(IOApiTests, MultilineOutputTest) {
85   // Allocate buffer for multiline output
86   static constexpr int numLines{5};
87   static constexpr int lineLength{32};
88   char buffer[numLines][lineLength];
89 
90   // Create descriptor for entire buffer
91   static constexpr int staticDescriptorMaxRank{1};
92   StaticDescriptor<staticDescriptorMaxRank> wholeStaticDescriptor;
93   Descriptor &whole{wholeStaticDescriptor.descriptor()};
94   static const SubscriptValue extent[]{numLines};
95   whole.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength, &buffer,
96       staticDescriptorMaxRank, extent, CFI_attribute_pointer);
97   whole.Dump(stderr);
98   whole.Check();
99 
100   // Create descriptor for buffer section
101   StaticDescriptor<staticDescriptorMaxRank> sectionStaticDescriptor;
102   Descriptor &section{sectionStaticDescriptor.descriptor()};
103   static const SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1};
104   section.Establish(whole.type(), /*elementBytes=*/whole.ElementBytes(),
105       nullptr, /*maxRank=*/staticDescriptorMaxRank, extent,
106       CFI_attribute_pointer);
107 
108   // Ensure C descriptor address `section.raw()` is updated without error
109   const auto error{
110       CFI_section(&section.raw(), &whole.raw(), lowers, uppers, strides)};
111   ASSERT_EQ(error, 0) << "multiline: CFI_section failed: " << error;
112   section.Dump(stderr);
113   section.Check();
114 
115   // Create format string and initialize IO operation
116   const char *format{
117       "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"};
118   auto cookie{IONAME(BeginInternalArrayFormattedOutput)(
119       section, format, std::strlen(format))};
120 
121   // Fill last line with periods
122   std::memset(buffer[numLines - 1], '.', lineLength);
123 
124   // Write data to buffer
125   IONAME(OutputAscii)(cookie, "WORLD", 5);
126   IONAME(OutputAscii)(cookie, "HELLO", 5);
127   IONAME(OutputInteger64)(cookie, 789);
128   for (int j{666}; j <= 999; j += 111) {
129     IONAME(OutputInteger64)(cookie, j);
130   }
131 
132   // Ensure no errors occured in write operations above
133   const auto status{IONAME(EndIoStatement)(cookie)};
134   ASSERT_EQ(status, 0) << "multiline: '" << format << "' failed, status "
135                        << static_cast<int>(status);
136 
137   static const std::string expect{">HELLO, WORLD                  <"
138                                   "                                "
139                                   "789                 abcd 666 777"
140                                   " 888 999                        "
141                                   "................................"};
142   // Ensure formatted string matches expected output
143   EXPECT_TRUE(
144       CompareFormattedStrings(expect, std::string{buffer[0], sizeof buffer}))
145       << "Expected '" << expect << "' but got '"
146       << std::string{buffer[0], sizeof buffer} << "'";
147 }
148 
149 TEST(IOApiTests, ListInputTest) {
150   static const char input[]{",1*,(5.,6.),(7.0,8.0)"};
151   auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)};
152 
153   // Create real values for IO tests
154   static constexpr int numRealValues{8};
155   float z[numRealValues];
156   for (int j{0}; j < numRealValues; ++j) {
157     z[j] = -(j + 1);
158   }
159 
160   // Ensure reading complex values to floats does not result in an error
161   for (int j{0}; j < numRealValues; j += 2) {
162     ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j]))
163         << "InputComplex32 failed with value " << z[j];
164   }
165 
166   // Ensure no IO errors occured during IO operations above
167   auto status{IONAME(EndIoStatement)(cookie)};
168   ASSERT_EQ(status, 0) << "Failed complex list-directed input, status "
169                        << static_cast<int>(status);
170 
171   // Ensure writing complex values from floats does not result in an error
172   static constexpr int bufferSize{39};
173   char output[bufferSize];
174   output[bufferSize - 1] = '\0';
175   cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1);
176   for (int j{0}; j < numRealValues; j += 2) {
177     ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1]))
178         << "OutputComplex32 failed when outputting value " << z[j] << ", "
179         << z[j + 1];
180   }
181 
182   // Ensure no IO errors occured during IO operations above
183   status = IONAME(EndIoStatement)(cookie);
184   ASSERT_EQ(status, 0) << "Failed complex list-directed output, status "
185                        << static_cast<int>(status);
186 
187   // Verify output buffer against expected value
188   static const char expect[bufferSize]{
189       " (-1.,-2.) (-3.,-4.) (5.,6.) (7.,8.)  "};
190   ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0)
191       << "Failed complex list-directed output, expected '" << expect
192       << "', but got '" << output << "'";
193 }
194 
195 TEST(IOApiTests, DescriptorOutputTest) {
196   static constexpr int bufferSize{10};
197   char buffer[bufferSize];
198   const char *format{"(2A4)"};
199   auto cookie{IONAME(BeginInternalFormattedOutput)(
200       buffer, bufferSize, format, std::strlen(format))};
201 
202   // Create descriptor for output
203   static constexpr int staticDescriptorMaxRank{1};
204   StaticDescriptor<staticDescriptorMaxRank> staticDescriptor;
205   Descriptor &desc{staticDescriptor.descriptor()};
206   static constexpr int subscriptExtent{2};
207   static const SubscriptValue extent[]{subscriptExtent};
208 
209   // Manually write to descriptor buffer
210   static constexpr int dataLength{4};
211   char data[subscriptExtent][dataLength];
212   std::memcpy(data[0], "ABCD", dataLength);
213   std::memcpy(data[1], "EFGH", dataLength);
214   desc.Establish(TypeCode{CFI_type_char}, dataLength, &data,
215       staticDescriptorMaxRank, extent);
216   desc.Dump(stderr);
217   desc.Check();
218   IONAME(OutputDescriptor)(cookie, desc);
219 
220   // Ensure no errors were encountered in initializing the cookie and descriptor
221   auto formatStatus{IONAME(EndIoStatement)(cookie)};
222   ASSERT_EQ(formatStatus, 0)
223       << "descrOutputTest: '" << format << "' failed, status "
224       << static_cast<int>(formatStatus);
225 
226   // Ensure buffer matches expected output
227   EXPECT_TRUE(
228       CompareFormattedStrings("ABCDEFGH  ", std::string{buffer, sizeof buffer}))
229       << "descrOutputTest: formatted: got '"
230       << std::string{buffer, sizeof buffer} << "'";
231 
232   // Begin list-directed output on cookie by descriptor
233   cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer);
234   IONAME(OutputDescriptor)(cookie, desc);
235 
236   // Ensure list-directed output does not result in an IO error
237   auto listDirectedStatus{IONAME(EndIoStatement)(cookie)};
238   ASSERT_EQ(listDirectedStatus, 0)
239       << "descrOutputTest: list-directed failed, status "
240       << static_cast<int>(listDirectedStatus);
241 
242   // Ensure buffer matches expected output
243   EXPECT_TRUE(
244       CompareFormattedStrings(" ABCDEFGH ", std::string{buffer, sizeof buffer}))
245       << "descrOutputTest: list-directed: got '"
246       << std::string{buffer, sizeof buffer} << "'";
247 }
248 
249 //------------------------------------------------------------------------------
250 /// Tests for output formatting real values
251 //------------------------------------------------------------------------------
252 
253 TEST(IOApiTests, FormatZeroes) {
254   static constexpr std::pair<const char *, const char *> zeroes[]{
255       {"(E32.17,';')", "         0.00000000000000000E+00;"},
256       {"(F32.17,';')", "             0.00000000000000000;"},
257       {"(G32.17,';')", "          0.0000000000000000    ;"},
258       {"(DC,E32.17,';')", "         0,00000000000000000E+00;"},
259       {"(DC,F32.17,';')", "             0,00000000000000000;"},
260       {"(DC,G32.17,';')", "          0,0000000000000000    ;"},
261       {"(D32.17,';')", "         0.00000000000000000D+00;"},
262       {"(E32.17E1,';')", "          0.00000000000000000E+0;"},
263       {"(G32.17E1,';')", "           0.0000000000000000   ;"},
264       {"(E32.17E0,';')", "          0.00000000000000000E+0;"},
265       {"(G32.17E0,';')", "          0.0000000000000000    ;"},
266       {"(1P,E32.17,';')", "         0.00000000000000000E+00;"},
267       {"(1PE32.17,';')", "         0.00000000000000000E+00;"}, // no comma
268       {"(1P,F32.17,';')", "             0.00000000000000000;"},
269       {"(1P,G32.17,';')", "          0.0000000000000000    ;"},
270       {"(2P,E32.17,';')", "         00.0000000000000000E+00;"},
271       {"(-1P,E32.17,';')", "         0.00000000000000000E+00;"},
272       {"(G0,';')", "0.;"},
273   };
274 
275   for (auto const &[format, expect] : zeroes) {
276     ASSERT_TRUE(CompareFormatReal(format, 0.0, expect))
277         << "Failed to format " << format << ", expected " << expect;
278   }
279 }
280 
281 TEST(IOApiTests, FormatOnes) {
282   static constexpr std::pair<const char *, const char *> ones[]{
283       {"(E32.17,';')", "         0.10000000000000000E+01;"},
284       {"(F32.17,';')", "             1.00000000000000000;"},
285       {"(G32.17,';')", "          1.0000000000000000    ;"},
286       {"(E32.17E1,';')", "          0.10000000000000000E+1;"},
287       {"(G32.17E1,';')", "           1.0000000000000000   ;"},
288       {"(E32.17E0,';')", "          0.10000000000000000E+1;"},
289       {"(G32.17E0,';')", "          1.0000000000000000    ;"},
290       {"(E32.17E4,';')", "       0.10000000000000000E+0001;"},
291       {"(G32.17E4,';')", "        1.0000000000000000      ;"},
292       {"(1P,E32.17,';')", "         1.00000000000000000E+00;"},
293       {"(1PE32.17,';')", "         1.00000000000000000E+00;"}, // no comma
294       {"(1P,F32.17,';')", "            10.00000000000000000;"},
295       {"(1P,G32.17,';')", "          1.0000000000000000    ;"},
296       {"(ES32.17,';')", "         1.00000000000000000E+00;"},
297       {"(2P,E32.17,';')", "         10.0000000000000000E-01;"},
298       {"(2P,G32.17,';')", "          1.0000000000000000    ;"},
299       {"(-1P,E32.17,';')", "         0.01000000000000000E+02;"},
300       {"(-1P,G32.17,';')", "          1.0000000000000000    ;"},
301       {"(G0,';')", "1.;"},
302   };
303 
304   for (auto const &[format, expect] : ones) {
305     ASSERT_TRUE(CompareFormatReal(format, 1.0, expect))
306         << "Failed to format " << format << ", expected " << expect;
307   }
308 }
309 
310 TEST(IOApiTests, FormatNegativeOnes) {
311   static constexpr std::tuple<const char *, const char *> negOnes[]{
312       {"(E32.17,';')", "        -0.10000000000000000E+01;"},
313       {"(F32.17,';')", "            -1.00000000000000000;"},
314       {"(G32.17,';')", "         -1.0000000000000000    ;"},
315       {"(G0,';')", "-1.;"},
316   };
317   for (auto const &[format, expect] : negOnes) {
318     ASSERT_TRUE(CompareFormatReal(format, -1.0, expect))
319         << "Failed to format " << format << ", expected " << expect;
320   }
321 }
322 
323 // Each test case contains a raw uint64, a format string for a real value, and
324 // the expected resulting string from formatting the raw uint64. The double
325 // representation of the uint64 is commented above each test case.
326 TEST(IOApiTests, FormatDoubleValues) {
327 
328   using TestCaseTy = std::tuple<std::uint64_t,
329       std::vector<std::tuple<const char *, const char *>>>;
330   static const std::vector<TestCaseTy> testCases{
331       {// -0
332           0x8000000000000000,
333           {
334               {"(E9.1,';')", " -0.0E+00;"},
335               {"(F4.0,';')", " -0.;"},
336               {"(F0.1,';')", "-.0;"},
337               {"(G8.0,';')", "-0.0E+00;"},
338               {"(G8.1,';')", " -0.    ;"},
339               {"(G0,';')", "-0.;"},
340               {"(E9.1,';')", " -0.0E+00;"},
341           }},
342       {// +Inf
343           0x7ff0000000000000,
344           {
345               {"(E9.1,';')", "      Inf;"},
346               {"(F9.1,';')", "      Inf;"},
347               {"(G9.1,';')", "      Inf;"},
348               {"(SP,E9.1,';')", "     +Inf;"},
349               {"(SP,F9.1,';')", "     +Inf;"},
350               {"(SP,G9.1,';')", "     +Inf;"},
351               {"(G0,';')", "Inf;"},
352           }},
353       {// -Inf
354           0xfff0000000000000,
355           {
356               {"(E9.1,';')", "     -Inf;"},
357               {"(F9.1,';')", "     -Inf;"},
358               {"(G9.1,';')", "     -Inf;"},
359               {"(G0,';')", "-Inf;"},
360           }},
361       {// NaN
362           0x7ff0000000000001,
363           {
364               {"(E9.1,';')", "      NaN;"},
365               {"(F9.1,';')", "      NaN;"},
366               {"(G9.1,';')", "      NaN;"},
367               {"(G0,';')", "NaN;"},
368           }},
369       {// NaN (sign irrelevant)
370           0xfff0000000000001,
371           {
372               {"(E9.1,';')", "      NaN;"},
373               {"(F9.1,';')", "      NaN;"},
374               {"(G9.1,';')", "      NaN;"},
375               {"(SP,E9.1,';')", "      NaN;"},
376               {"(SP,F9.1,';')", "      NaN;"},
377               {"(SP,G9.1,';')", "      NaN;"},
378               {"(G0,';')", "NaN;"},
379           }},
380       {// 0.1 rounded
381           0x3fb999999999999a,
382           {
383               {"(E62.55,';')",
384                   " 0.1000000000000000055511151231257827021181583404541015625E+"
385                   "00;"},
386               {"(E0.0,';')", "0.E+00;"},
387               {"(E0.55,';')",
388                   "0.1000000000000000055511151231257827021181583404541015625E+"
389                   "00;"},
390               {"(E0,';')", ".1E+00;"},
391               {"(F58.55,';')",
392                   " 0."
393                   "1000000000000000055511151231257827021181583404541015625;"},
394               {"(F0.0,';')", "0.;"},
395               {"(F0.55,';')",
396                   ".1000000000000000055511151231257827021181583404541015625;"},
397               {"(F0,';')", ".1;"},
398               {"(G62.55,';')",
399                   " 0.1000000000000000055511151231257827021181583404541015625  "
400                   "  ;"},
401               {"(G0.0,';')", "0.;"},
402               {"(G0.55,';')",
403                   ".1000000000000000055511151231257827021181583404541015625;"},
404               {"(G0,';')", ".1;"},
405           }},
406       {// 1.5
407           0x3ff8000000000000,
408           {
409               {"(E9.2,';')", " 0.15E+01;"},
410               {"(F4.1,';')", " 1.5;"},
411               {"(G7.1,';')", " 2.    ;"},
412               {"(RN,E8.1,';')", " 0.2E+01;"},
413               {"(RN,F3.0,';')", " 2.;"},
414               {"(RN,G7.0,';')", " 0.E+01;"},
415               {"(RN,G7.1,';')", " 2.    ;"},
416               {"(RD,E8.1,';')", " 0.1E+01;"},
417               {"(RD,F3.0,';')", " 1.;"},
418               {"(RD,G7.0,';')", " 0.E+01;"},
419               {"(RD,G7.1,';')", " 1.    ;"},
420               {"(RU,E8.1,';')", " 0.2E+01;"},
421               {"(RU,G7.0,';')", " 0.E+01;"},
422               {"(RU,G7.1,';')", " 2.    ;"},
423               {"(RZ,E8.1,';')", " 0.1E+01;"},
424               {"(RZ,F3.0,';')", " 1.;"},
425               {"(RZ,G7.0,';')", " 0.E+01;"},
426               {"(RZ,G7.1,';')", " 1.    ;"},
427               {"(RC,E8.1,';')", " 0.2E+01;"},
428               {"(RC,F3.0,';')", " 2.;"},
429               {"(RC,G7.0,';')", " 0.E+01;"},
430               {"(RC,G7.1,';')", " 2.    ;"},
431           }},
432       {// -1.5
433           0xbff8000000000000,
434           {
435               {"(E9.2,';')", "-0.15E+01;"},
436               {"(RN,E8.1,';')", "-0.2E+01;"},
437               {"(RD,E8.1,';')", "-0.2E+01;"},
438               {"(RU,E8.1,';')", "-0.1E+01;"},
439               {"(RZ,E8.1,';')", "-0.1E+01;"},
440               {"(RC,E8.1,';')", "-0.2E+01;"},
441           }},
442       {// 2.5
443           0x4004000000000000,
444           {
445               {"(E9.2,';')", " 0.25E+01;"},
446               {"(RN,E8.1,';')", " 0.2E+01;"},
447               {"(RD,E8.1,';')", " 0.2E+01;"},
448               {"(RU,E8.1,';')", " 0.3E+01;"},
449               {"(RZ,E8.1,';')", " 0.2E+01;"},
450               {"(RC,E8.1,';')", " 0.3E+01;"},
451           }},
452       {// -2.5
453           0xc004000000000000,
454           {
455               {"(E9.2,';')", "-0.25E+01;"},
456               {"(RN,E8.1,';')", "-0.2E+01;"},
457               {"(RD,E8.1,';')", "-0.3E+01;"},
458               {"(RU,E8.1,';')", "-0.2E+01;"},
459               {"(RZ,E8.1,';')", "-0.2E+01;"},
460               {"(RC,E8.1,';')", "-0.3E+01;"},
461           }},
462       {// least positive nonzero subnormal
463           1,
464           {
465               {"(E32.17,';')", "         0.49406564584124654-323;"},
466               {"(ES32.17,';')", "         4.94065645841246544-324;"},
467               {"(EN32.17,';')", "         4.94065645841246544-324;"},
468               {"(E759.752,';')",
469                   " 0."
470                   "494065645841246544176568792868221372365059802614324764425585"
471                   "682500675507270208751865299836361635992379796564695445717730"
472                   "926656710355939796398774796010781878126300713190311404527845"
473                   "817167848982103688718636056998730723050006387409153564984387"
474                   "312473397273169615140031715385398074126238565591171026658556"
475                   "686768187039560310624931945271591492455329305456544401127480"
476                   "129709999541931989409080416563324524757147869014726780159355"
477                   "238611550134803526493472019379026810710749170333222684475333"
478                   "572083243193609238289345836806010601150616980975307834227731"
479                   "832924790498252473077637592724787465608477820373446969953364"
480                   "701797267771758512566055119913150489110145103786273816725095"
481                   "583738973359899366480994116420570263709027924276754456522908"
482                   "75386825064197182655334472656250-323;"},
483               {"(G0,';')", ".5-323;"},
484               {"(E757.750,';')",
485                   " 0."
486                   "494065645841246544176568792868221372365059802614324764425585"
487                   "682500675507270208751865299836361635992379796564695445717730"
488                   "926656710355939796398774796010781878126300713190311404527845"
489                   "817167848982103688718636056998730723050006387409153564984387"
490                   "312473397273169615140031715385398074126238565591171026658556"
491                   "686768187039560310624931945271591492455329305456544401127480"
492                   "129709999541931989409080416563324524757147869014726780159355"
493                   "238611550134803526493472019379026810710749170333222684475333"
494                   "572083243193609238289345836806010601150616980975307834227731"
495                   "832924790498252473077637592724787465608477820373446969953364"
496                   "701797267771758512566055119913150489110145103786273816725095"
497                   "583738973359899366480994116420570263709027924276754456522908"
498                   "753868250641971826553344726562-323;"},
499               {"(RN,E757.750,';')",
500                   " 0."
501                   "494065645841246544176568792868221372365059802614324764425585"
502                   "682500675507270208751865299836361635992379796564695445717730"
503                   "926656710355939796398774796010781878126300713190311404527845"
504                   "817167848982103688718636056998730723050006387409153564984387"
505                   "312473397273169615140031715385398074126238565591171026658556"
506                   "686768187039560310624931945271591492455329305456544401127480"
507                   "129709999541931989409080416563324524757147869014726780159355"
508                   "238611550134803526493472019379026810710749170333222684475333"
509                   "572083243193609238289345836806010601150616980975307834227731"
510                   "832924790498252473077637592724787465608477820373446969953364"
511                   "701797267771758512566055119913150489110145103786273816725095"
512                   "583738973359899366480994116420570263709027924276754456522908"
513                   "753868250641971826553344726562-323;"},
514               {"(RD,E757.750,';')",
515                   " 0."
516                   "494065645841246544176568792868221372365059802614324764425585"
517                   "682500675507270208751865299836361635992379796564695445717730"
518                   "926656710355939796398774796010781878126300713190311404527845"
519                   "817167848982103688718636056998730723050006387409153564984387"
520                   "312473397273169615140031715385398074126238565591171026658556"
521                   "686768187039560310624931945271591492455329305456544401127480"
522                   "129709999541931989409080416563324524757147869014726780159355"
523                   "238611550134803526493472019379026810710749170333222684475333"
524                   "572083243193609238289345836806010601150616980975307834227731"
525                   "832924790498252473077637592724787465608477820373446969953364"
526                   "701797267771758512566055119913150489110145103786273816725095"
527                   "583738973359899366480994116420570263709027924276754456522908"
528                   "753868250641971826553344726562-323;"},
529               {"(RU,E757.750,';')",
530                   " 0."
531                   "494065645841246544176568792868221372365059802614324764425585"
532                   "682500675507270208751865299836361635992379796564695445717730"
533                   "926656710355939796398774796010781878126300713190311404527845"
534                   "817167848982103688718636056998730723050006387409153564984387"
535                   "312473397273169615140031715385398074126238565591171026658556"
536                   "686768187039560310624931945271591492455329305456544401127480"
537                   "129709999541931989409080416563324524757147869014726780159355"
538                   "238611550134803526493472019379026810710749170333222684475333"
539                   "572083243193609238289345836806010601150616980975307834227731"
540                   "832924790498252473077637592724787465608477820373446969953364"
541                   "701797267771758512566055119913150489110145103786273816725095"
542                   "583738973359899366480994116420570263709027924276754456522908"
543                   "753868250641971826553344726563-323;"},
544               {"(RC,E757.750,';')",
545                   " 0."
546                   "494065645841246544176568792868221372365059802614324764425585"
547                   "682500675507270208751865299836361635992379796564695445717730"
548                   "926656710355939796398774796010781878126300713190311404527845"
549                   "817167848982103688718636056998730723050006387409153564984387"
550                   "312473397273169615140031715385398074126238565591171026658556"
551                   "686768187039560310624931945271591492455329305456544401127480"
552                   "129709999541931989409080416563324524757147869014726780159355"
553                   "238611550134803526493472019379026810710749170333222684475333"
554                   "572083243193609238289345836806010601150616980975307834227731"
555                   "832924790498252473077637592724787465608477820373446969953364"
556                   "701797267771758512566055119913150489110145103786273816725095"
557                   "583738973359899366480994116420570263709027924276754456522908"
558                   "753868250641971826553344726563-323;"},
559           }},
560       {// least positive nonzero normal
561           0x10000000000000,
562           {
563               {"(E723.716,';')",
564                   " 0."
565                   "222507385850720138309023271733240406421921598046233183055332"
566                   "741688720443481391819585428315901251102056406733973103581100"
567                   "515243416155346010885601238537771882113077799353200233047961"
568                   "014744258363607192156504694250373420837525080665061665815894"
569                   "872049117996859163964850063590877011830487479978088775374994"
570                   "945158045160505091539985658247081864511353793580499211598108"
571                   "576605199243335211435239014879569960959128889160299264151106"
572                   "346631339366347758651302937176204732563178148566435087212282"
573                   "863764204484681140761391147706280168985324411002416144742161"
574                   "856716615054015428508471675290190316132277889672970737312333"
575                   "408698898317506783884692609277397797285865965494109136909540"
576                   "61364675687023986783152906809846172109246253967285156250-"
577                   "307;"},
578               {"(G0,';')", ".22250738585072014-307;"},
579           }},
580       {// greatest finite
581           0x7fefffffffffffffuLL,
582           {
583               {"(E32.17,';')", "         0.17976931348623157+309;"},
584               {"(E317.310,';')",
585                   " 0."
586                   "179769313486231570814527423731704356798070567525844996598917"
587                   "476803157260780028538760589558632766878171540458953514382464"
588                   "234321326889464182768467546703537516986049910576551282076245"
589                   "490090389328944075868508455133942304583236903222948165808559"
590                   "332123348274797826204144723168738177180919299881250404026184"
591                   "1248583680+309;"},
592               {"(ES317.310,';')",
593                   " 1."
594                   "797693134862315708145274237317043567980705675258449965989174"
595                   "768031572607800285387605895586327668781715404589535143824642"
596                   "343213268894641827684675467035375169860499105765512820762454"
597                   "900903893289440758685084551339423045832369032229481658085593"
598                   "321233482747978262041447231687381771809192998812504040261841"
599                   "2485836800+308;"},
600               {"(EN319.310,';')",
601                   " 179."
602                   "769313486231570814527423731704356798070567525844996598917476"
603                   "803157260780028538760589558632766878171540458953514382464234"
604                   "321326889464182768467546703537516986049910576551282076245490"
605                   "090389328944075868508455133942304583236903222948165808559332"
606                   "123348274797826204144723168738177180919299881250404026184124"
607                   "8583680000+306;"},
608               {"(G0,';')", ".17976931348623157+309;"},
609           }},
610   };
611 
612   for (auto const &[value, cases] : testCases) {
613     for (auto const &[format, expect] : cases) {
614       ASSERT_TRUE(CompareFormatReal(format, value, expect))
615           << "Failed to format " << format << ", expected " << expect;
616     }
617   }
618 
619   using IndividualTestCaseTy = std::tuple<const char *, double, const char *>;
620   static const std::vector<IndividualTestCaseTy> individualTestCases{
621       {"(F5.3,';')", 25., "*****;"},
622       {"(F5.3,';')", 2.5, "2.500;"},
623       {"(F5.3,';')", 0.25, "0.250;"},
624       {"(F5.3,';')", 0.025, "0.025;"},
625       {"(F5.3,';')", 0.0025, "0.003;"},
626       {"(F5.3,';')", 0.00025, "0.000;"},
627       {"(F5.3,';')", 0.000025, "0.000;"},
628       {"(F5.3,';')", -25., "*****;"},
629       {"(F5.3,';')", -2.5, "*****;"},
630       {"(F5.3,';')", -0.25, "-.250;"},
631       {"(F5.3,';')", -0.025, "-.025;"},
632       {"(F5.3,';')", -0.0025, "-.003;"},
633       {"(F5.3,';')", -0.00025, "-.000;"},
634       {"(F5.3,';')", -0.000025, "-.000;"},
635       {"(F5.3,';')", 99.999, "*****;"},
636       {"(F5.3,';')", 9.9999, "*****;"},
637       {"(F5.3,';')", 0.99999, "1.000;"},
638       {"(F5.3,';')", 0.099999, "0.100;"},
639       {"(F5.3,';')", 0.0099999, "0.010;"},
640       {"(F5.3,';')", 0.00099999, "0.001;"},
641       {"(F5.3,';')", 0.0005, "0.001;"},
642       {"(F5.3,';')", 0.00049999, "0.000;"},
643       {"(F5.3,';')", 0.000099999, "0.000;"},
644       {"(F5.3,';')", -99.999, "*****;"},
645       {"(F5.3,';')", -9.9999, "*****;"},
646       {"(F5.3,';')", -0.99999, "*****;"},
647       {"(F5.3,';')", -0.099999, "-.100;"},
648       {"(F5.3,';')", -0.0099999, "-.010;"},
649       {"(F5.3,';')", -0.00099999, "-.001;"},
650       {"(F5.3,';')", -0.0005, "-.001;"},
651       {"(F5.3,';')", -0.00049999, "-.000;"},
652       {"(F5.3,';')", -0.000099999, "-.000;"},
653       {"(F0.1,';')", 0.0, ".0;"},
654   };
655 
656   for (auto const &[format, value, expect] : individualTestCases) {
657     ASSERT_TRUE(CompareFormatReal(format, value, expect))
658         << "Failed to format " << format << ", expected " << expect;
659   }
660 }
661 
662 //------------------------------------------------------------------------------
663 /// Tests for input formatting real values
664 //------------------------------------------------------------------------------
665 
666 // Ensure double input values correctly map to raw uint64 values
667 TEST(IOApiTests, FormatDoubleInputValues) {
668   using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t>;
669   static const std::vector<TestCaseTy> testCases{
670       {"(F18.0)", "                 0", 0x0},
671       {"(F18.0)", "                  ", 0x0},
672       {"(F18.0)", "                -0", 0x8000000000000000},
673       {"(F18.0)", "                01", 0x3ff0000000000000},
674       {"(F18.0)", "                 1", 0x3ff0000000000000},
675       {"(F18.0)", "              125.", 0x405f400000000000},
676       {"(F18.0)", "              12.5", 0x4029000000000000},
677       {"(F18.0)", "              1.25", 0x3ff4000000000000},
678       {"(F18.0)", "             01.25", 0x3ff4000000000000},
679       {"(F18.0)", "              .125", 0x3fc0000000000000},
680       {"(F18.0)", "             0.125", 0x3fc0000000000000},
681       {"(F18.0)", "             .0625", 0x3fb0000000000000},
682       {"(F18.0)", "            0.0625", 0x3fb0000000000000},
683       {"(F18.0)", "               125", 0x405f400000000000},
684       {"(F18.1)", "               125", 0x4029000000000000},
685       {"(F18.2)", "               125", 0x3ff4000000000000},
686       {"(F18.3)", "               125", 0x3fc0000000000000},
687       {"(-1P,F18.0)", "               125", 0x4093880000000000}, // 1250
688       {"(1P,F18.0)", "               125", 0x4029000000000000}, // 12.5
689       {"(BZ,F18.0)", "              125 ", 0x4093880000000000}, // 1250
690       {"(BZ,F18.0)", "       125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13
691       {"(DC,F18.0)", "              12,5", 0x4029000000000000},
692   };
693   for (auto const &[format, data, want] : testCases) {
694     auto cookie{IONAME(BeginInternalFormattedInput)(
695         data, std::strlen(data), format, std::strlen(format))};
696     union {
697       double x;
698       std::uint64_t raw;
699     } u;
700     u.raw = 0;
701 
702     // Read buffer into union value
703     IONAME(EnableHandlers)(cookie, true, true, true, true, true);
704     IONAME(InputReal64)(cookie, u.x);
705 
706     static constexpr int bufferSize{65};
707     char iomsg[bufferSize];
708     std::memset(iomsg, '\0', bufferSize - 1);
709 
710     // Ensure no errors were encountered reading input buffer into union value
711     IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1);
712     auto status{IONAME(EndIoStatement)(cookie)};
713     ASSERT_EQ(status, 0) << '\'' << format << "' failed reading '" << data
714                          << "', status " << static_cast<int>(status)
715                          << " iomsg '" << iomsg << "'";
716 
717     // Ensure raw uint64 value matches expected conversion from double
718     ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data
719                            << "', want " << want << ", got " << u.raw;
720   }
721 }
722