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