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 §ion{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(§ion.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