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
18using namespace Fortran::runtime;
19using namespace Fortran::runtime::io;
20
21static bool CompareFormattedStrings(
22 const std::string &expect, const std::string &got) {
23 std::string want{expect};
24 want.resize(n: got.size(), c: ' ');
25 return want == got;
26}
27
28static bool CompareFormattedStrings(
29 const char *expect, const std::string &&got) {
30 return CompareFormattedStrings(expect: std::string(expect), got: std::move(got));
31}
32
33// Perform format and compare the result with expected value
34static 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(s: " ")};
44 if (lastNonBlank != std::string::npos) {
45 got.resize(n: lastNonBlank + 1);
46 }
47 return CompareFormattedStrings(expect, got);
48}
49
50// Convert raw uint64 into double, perform format, and compare with expected
51static 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(dest: &x, src: &xInt, n: sizeof xInt);
57 return CompareFormatReal(format, x, expect, got);
58}
59
60static 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(s: " ")};
70 if (lastNonBlank != std::string::npos) {
71 got.resize(n: lastNonBlank + 1);
72 }
73 return CompareFormattedStrings(expect, got);
74}
75
76struct IOApiTests : CrashHandlerFixture {};
77
78TEST(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
105TEST(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(s: buffer[numLines - 1], c: '.', n: 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
170TEST(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
216TEST(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(dest: data[0], src: "ABCD", n: dataLength);
234 std::memcpy(dest: data[1], src: "EFGH", n: 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
274TEST(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
306TEST(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
339TEST(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.
358TEST(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 };
757
758 for (auto const &[format, value, expect] : individualTestCases) {
759 std::string got;
760 char hex[17];
761 std::snprintf(s: hex, maxlen: sizeof hex, format: "%016llx",
762 *reinterpret_cast<const unsigned long long *>(&value));
763 ASSERT_TRUE(CompareFormatReal(format, value, expect, got))
764 << "Failed to format " << value << " 0x" << hex << " with format "
765 << format << ", expected '" << expect << "', got '" << got << "'";
766 }
767
768 // Problematic EN formatting edge cases with rounding
769 using IndividualENTestCaseTy = std::tuple<std::uint64_t, const char *>;
770 static const std::vector<IndividualENTestCaseTy> individualENTestCases{
771 {0x3E11183197785F8C, " 995.0E-12"}, // 0.9950312500000000115852E-09
772 {0x3E11180E68455D30, " 995.0E-12"}, // 0.9949999999999999761502E-09
773 {0x3E112BD8F4F6B0D7, " 999.5E-12"}, // 0.9994999999999999089118E-09
774 {0x3E45794883CA8782, " 10.0E-09"}, // 0.9999499999999999642266E-08
775 {0x3F506218230C7482, " 999.9E-06"}, // 0.9999499999999998840761E-03
776 {0x3FB99652BD3C3612, " 100.0E-03"}, // 0.9999500000000000055067E+00
777 {0x4023E66666666667, " 10.0E+00"}, // 0.9950000000000001065814E+01
778 };
779
780 for (auto const &[value, expect] : individualENTestCases) {
781 std::string got;
782 ASSERT_TRUE(CompareFormatReal("(EN10.1)", value, expect, got))
783 << "Failed to format EN10.1, expected '" << expect << "', got '" << got
784 << "'";
785 }
786}
787
788TEST(IOApiTests, FormatIntegerValues) {
789 using IntTestCaseTy = std::tuple<const char *, std::int64_t, const char *>;
790 static const std::vector<IntTestCaseTy> intTestCases{
791 {"(I4)", 0, " 0"},
792 {"(I4)", 1, " 1"},
793 {"(I4)", 9999, "9999"},
794 {"(SP,I4)", 1, " +1"},
795 {"(SP,I4)", 9999, "****"},
796 {"(SP,I4)", 999, "+999"},
797 {"(I4)", -1, " -1"},
798 {"(I4)", -9999, "****"},
799 {"(I4)", -999, "-999"},
800 {"(I4.2)", 1, " 01"},
801 {"(I4.2)", -1, " -01"},
802 {"(I4.2)", 999, " 999"},
803 {"(I4.4)", 999, "0999"},
804 {"(I0)", 0, "0"},
805 {"(I0)", 1, "1"},
806 {"(I0)", 9999, "9999"},
807 {"(SP,I0)", 1, "+1"},
808 {"(SP,I0)", 9999, "+9999"},
809 {"(SP,I0)", 999, "+999"},
810 {"(I0)", -1, "-1"},
811 {"(I0)", -9999, "-9999"},
812 {"(I0)", -999, "-999"},
813 {"(I0.2)", 1, "01"},
814 {"(I0.2)", -1, "-01"},
815 {"(I0.2)", 999, "999"},
816 {"(I0.4)", 999, "0999"},
817 {"(G4)", 0, " 0"},
818 {"(G4)", 1, " 1"},
819 {"(G4)", 9999, "9999"},
820 {"(SP,G4)", 1, " +1"},
821 {"(SP,G4)", 9999, "****"},
822 {"(SP,G4)", 999, "+999"},
823 {"(G4)", -1, " -1"},
824 {"(G4)", -9999, "****"},
825 {"(G4)", -999, "-999"},
826 {"(G4.2)", 1, " 1"},
827 {"(G4.2)", -1, " -1"},
828 {"(G4.2)", 999, " 999"},
829 {"(G4.4)", 999, " 999"},
830 {"(G0)", 0, "0"},
831 {"(G0)", 1, "1"},
832 {"(G0)", 9999, "9999"},
833 {"(SP,G0)", 1, "+1"},
834 {"(SP,G0)", 9999, "+9999"},
835 {"(SP,G0)", 999, "+999"},
836 {"(G0)", -1, "-1"},
837 {"(G0)", -9999, "-9999"},
838 {"(G0)", -999, "-999"},
839 {"(G0.2)", 1, "1"},
840 {"(G0.2)", -1, "-1"},
841 {"(G0.2)", 999, "999"},
842 {"(G0.4)", 999, "999"},
843 };
844
845 for (auto const &[fmt, value, expect] : intTestCases) {
846 std::string got;
847 ASSERT_TRUE(CompareFormatInteger(fmt, value, expect, got))
848 << "Failed to format " << fmt << ", expected '" << expect << "', got '"
849 << got << "'";
850 }
851}
852
853//------------------------------------------------------------------------------
854/// Tests for input editing real values
855//------------------------------------------------------------------------------
856
857// Ensure double input values correctly map to raw uint64 values
858TEST(IOApiTests, EditDoubleInputValues) {
859 using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t, int>;
860 int ovf{IostatRealInputOverflow};
861 static const std::vector<TestCaseTy> testCases{
862 {"(F18.0)", " 0", 0x0, 0},
863 {"(F18.0)", " ", 0x0, 0},
864 {"(F18.0)", " -0", 0x8000000000000000, 0},
865 {"(F18.0)", " 01", 0x3ff0000000000000, 0},
866 {"(F18.0)", " 1", 0x3ff0000000000000, 0},
867 {"(F18.0)", " 125.", 0x405f400000000000, 0},
868 {"(F18.0)", " 12.5", 0x4029000000000000, 0},
869 {"(F18.0)", " 1.25", 0x3ff4000000000000, 0},
870 {"(F18.0)", " 01.25", 0x3ff4000000000000, 0},
871 {"(F18.0)", " .125", 0x3fc0000000000000, 0},
872 {"(F18.0)", " 0.125", 0x3fc0000000000000, 0},
873 {"(F18.0)", " .0625", 0x3fb0000000000000, 0},
874 {"(F18.0)", " 0.0625", 0x3fb0000000000000, 0},
875 {"(F18.0)", " 125", 0x405f400000000000, 0},
876 {"(F18.1)", " 125", 0x4029000000000000, 0},
877 {"(F18.2)", " 125", 0x3ff4000000000000, 0},
878 {"(F18.3)", " 125", 0x3fc0000000000000, 0},
879 {"(-1P,F18.0)", " 125", 0x4093880000000000, 0}, // 1250
880 {"(1P,F18.0)", " 125", 0x4029000000000000, 0}, // 12.5
881 {"(BZ,F18.0)", " 125 ", 0x4093880000000000, 0}, // 1250
882 {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000, 0}, // 1.25e13
883 {"(BZ,F18.0)", " . ", 0x0, 0},
884 {"(BZ,F18.0)", " . e +1 ", 0x0, 0},
885 {"(DC,F18.0)", " 12,5", 0x4029000000000000, 0},
886 {"(EX22.0)", "0X0P0 ", 0x0, 0}, // +0.
887 {"(EX22.0)", "-0X0P0 ", 0x8000000000000000, 0}, // -0.
888 {"(EX22.0)", "0X.8P1 ", 0x3ff0000000000000, 0}, // 1.0
889 {"(EX22.0)", "0X8.P-3 ", 0x3ff0000000000000, 0}, // 1.0
890 {"(EX22.0)", "0X.1P4 ", 0x3ff0000000000000, 0}, // 1.0
891 {"(EX22.0)", "0X10.P-4 ", 0x3ff0000000000000, 0}, // 1.0
892 {"(EX22.0)", "0X8.00P-3 ", 0x3ff0000000000000, 0}, // 1.0
893 {"(EX22.0)", "0X80.0P-6 ", 0x4000000000000000, 0}, // 2.0
894 {"(EX22.0)", "0XC.CCCCCCCCCCCDP-7 ", 0x3fb999999999999a, 0}, // 0.1
895 {"(EX22.0)", "0X.8P-1021 ", 0x0010000000000000,
896 0}, // min normal
897 {"(EX22.0)", "0X.8P-1022 ", 0x0008000000000000,
898 0}, // subnormal
899 {"(EX22.0)", "0X.8P-1073 ", 0x0000000000000001,
900 0}, // min subn.
901 {"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff,
902 0}, // max finite
903 {"(EX22.0)", "0X.8P1025 ", 0x7ff0000000000000, ovf}, // +Inf
904 {"(EX22.0)", "-0X.8P1025 ", 0xfff0000000000000, ovf}, // -Inf
905 {"(RC,EX22.0)", "0X1.0000000000000P0 ", 0x3ff0000000000000, 0},
906 {"(RC,EX22.0)", "0X1.00000000000008P0 ", 0x3ff0000000000001, 0},
907 {"(RC,EX22.0)", "0X1.000000000000008P0 ", 0x3ff0000000000000, 0},
908 {"(RC,EX22.0)", "0X1.00000000000004P0 ", 0x3ff0000000000000, 0},
909 {"(RC,EX22.0)", "0X.80000000000000P1 ", 0x3ff0000000000000, 0},
910 {"(RC,EX22.0)", "0X.80000000000004P1 ", 0x3ff0000000000001, 0},
911 {"(RC,EX22.0)", "0X.800000000000004P1 ", 0x3ff0000000000000, 0},
912 {"(RC,EX22.0)", "0X.80000000000002P1 ", 0x3ff0000000000000, 0},
913 {"(RZ,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE()
914 {"(RD,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE()
915 {"(RU,F7.0)", " 2.e308", 0x7ff0000000000000, ovf}, // +Inf
916 {"(RZ,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE()
917 {"(RD,F7.0)", "-2.e308", 0xfff0000000000000, ovf}, // -Inf
918 {"(RU,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE()
919 {"(RZ,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE()
920 {"(RD,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE()
921 {"(RU,F7.0)", " 1.e999", 0x7ff0000000000000, ovf}, // +Inf
922 {"(RZ,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE()
923 {"(RD,F7.0)", "-1.e999", 0xfff0000000000000, ovf}, // -Inf
924 {"(RU,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE()
925 {"(E9.1)", " 1.0E-325", 0x0, 0},
926 {"(RU,E9.1)", " 1.0E-325", 0x1, 0},
927 {"(E9.1)", "-1.0E-325", 0x8000000000000000, 0},
928 {"(RD,E9.1)", "-1.0E-325", 0x8000000000000001, 0},
929 };
930 for (auto const &[format, data, want, iostat] : testCases) {
931 auto cookie{IONAME(BeginInternalFormattedInput)(
932 data, std::strlen(data), format, std::strlen(format))};
933 union {
934 double x;
935 std::uint64_t raw;
936 } u;
937 u.raw = 0;
938
939 // Read buffer into union value
940 IONAME(EnableHandlers)(cookie, true, true, true, true, true);
941 IONAME(InputReal64)(cookie, u.x);
942
943 static constexpr int bufferSize{65};
944 char iomsg[bufferSize];
945 std::memset(s: iomsg, c: '\0', n: bufferSize - 1);
946
947 // Ensure no unexpected errors were encountered reading input buffer into
948 // union value
949 IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1);
950 auto status{IONAME(EndIoStatement)(cookie)};
951 ASSERT_EQ(status, iostat)
952 << '\'' << format << "' failed reading '" << data << "', status "
953 << static_cast<int>(status) << " != expected " << iostat << " iomsg '"
954 << iomsg << "'";
955
956 // Ensure raw uint64 value matches expected conversion from double
957 ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data
958 << "', want " << want << ", got " << u.raw;
959 }
960}
961

source code of flang/unittests/Runtime/NumericalFormatTest.cpp