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