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(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 {"(G8.0,';')", "-0.0E+00;"}, 337 {"(G8.1,';')", " -0. ;"}, 338 {"(G0,';')", "-0.;"}, 339 {"(E9.1,';')", " -0.0E+00;"}, 340 }}, 341 {// +Inf 342 0x7ff0000000000000, 343 { 344 {"(E9.1,';')", " Inf;"}, 345 {"(F9.1,';')", " Inf;"}, 346 {"(G9.1,';')", " Inf;"}, 347 {"(SP,E9.1,';')", " +Inf;"}, 348 {"(SP,F9.1,';')", " +Inf;"}, 349 {"(SP,G9.1,';')", " +Inf;"}, 350 {"(G0,';')", "Inf;"}, 351 }}, 352 {// -Inf 353 0xfff0000000000000, 354 { 355 {"(E9.1,';')", " -Inf;"}, 356 {"(F9.1,';')", " -Inf;"}, 357 {"(G9.1,';')", " -Inf;"}, 358 {"(G0,';')", "-Inf;"}, 359 }}, 360 {// NaN 361 0x7ff0000000000001, 362 { 363 {"(E9.1,';')", " NaN;"}, 364 {"(F9.1,';')", " NaN;"}, 365 {"(G9.1,';')", " NaN;"}, 366 {"(G0,';')", "NaN;"}, 367 }}, 368 {// NaN (sign irrelevant) 369 0xfff0000000000001, 370 { 371 {"(E9.1,';')", " NaN;"}, 372 {"(F9.1,';')", " NaN;"}, 373 {"(G9.1,';')", " NaN;"}, 374 {"(SP,E9.1,';')", " NaN;"}, 375 {"(SP,F9.1,';')", " NaN;"}, 376 {"(SP,G9.1,';')", " NaN;"}, 377 {"(G0,';')", "NaN;"}, 378 }}, 379 {// 0.1 rounded 380 0x3fb999999999999a, 381 { 382 {"(E62.55,';')", 383 " 0.1000000000000000055511151231257827021181583404541015625E+" 384 "00;"}, 385 {"(E0.0,';')", "0.E+00;"}, 386 {"(E0.55,';')", 387 "0.1000000000000000055511151231257827021181583404541015625E+" 388 "00;"}, 389 {"(E0,';')", ".1E+00;"}, 390 {"(F58.55,';')", 391 " 0." 392 "1000000000000000055511151231257827021181583404541015625;"}, 393 {"(F0.0,';')", "0.;"}, 394 {"(F0.55,';')", 395 ".1000000000000000055511151231257827021181583404541015625;"}, 396 {"(F0,';')", ".1;"}, 397 {"(G62.55,';')", 398 " 0.1000000000000000055511151231257827021181583404541015625 " 399 " ;"}, 400 {"(G0.0,';')", "0.;"}, 401 {"(G0.55,';')", 402 ".1000000000000000055511151231257827021181583404541015625;"}, 403 {"(G0,';')", ".1;"}, 404 }}, 405 {// 1.5 406 0x3ff8000000000000, 407 { 408 {"(E9.2,';')", " 0.15E+01;"}, 409 {"(F4.1,';')", " 1.5;"}, 410 {"(G7.1,';')", " 2. ;"}, 411 {"(RN,E8.1,';')", " 0.2E+01;"}, 412 {"(RN,F3.0,';')", " 2.;"}, 413 {"(RN,G7.0,';')", " 0.E+01;"}, 414 {"(RN,G7.1,';')", " 2. ;"}, 415 {"(RD,E8.1,';')", " 0.1E+01;"}, 416 {"(RD,F3.0,';')", " 1.;"}, 417 {"(RD,G7.0,';')", " 0.E+01;"}, 418 {"(RD,G7.1,';')", " 1. ;"}, 419 {"(RU,E8.1,';')", " 0.2E+01;"}, 420 {"(RU,G7.0,';')", " 0.E+01;"}, 421 {"(RU,G7.1,';')", " 2. ;"}, 422 {"(RZ,E8.1,';')", " 0.1E+01;"}, 423 {"(RZ,F3.0,';')", " 1.;"}, 424 {"(RZ,G7.0,';')", " 0.E+01;"}, 425 {"(RZ,G7.1,';')", " 1. ;"}, 426 {"(RC,E8.1,';')", " 0.2E+01;"}, 427 {"(RC,F3.0,';')", " 2.;"}, 428 {"(RC,G7.0,';')", " 0.E+01;"}, 429 {"(RC,G7.1,';')", " 2. ;"}, 430 }}, 431 {// -1.5 432 0xbff8000000000000, 433 { 434 {"(E9.2,';')", "-0.15E+01;"}, 435 {"(RN,E8.1,';')", "-0.2E+01;"}, 436 {"(RD,E8.1,';')", "-0.2E+01;"}, 437 {"(RU,E8.1,';')", "-0.1E+01;"}, 438 {"(RZ,E8.1,';')", "-0.1E+01;"}, 439 {"(RC,E8.1,';')", "-0.2E+01;"}, 440 }}, 441 {// 2.5 442 0x4004000000000000, 443 { 444 {"(E9.2,';')", " 0.25E+01;"}, 445 {"(RN,E8.1,';')", " 0.2E+01;"}, 446 {"(RD,E8.1,';')", " 0.2E+01;"}, 447 {"(RU,E8.1,';')", " 0.3E+01;"}, 448 {"(RZ,E8.1,';')", " 0.2E+01;"}, 449 {"(RC,E8.1,';')", " 0.3E+01;"}, 450 }}, 451 {// -2.5 452 0xc004000000000000, 453 { 454 {"(E9.2,';')", "-0.25E+01;"}, 455 {"(RN,E8.1,';')", "-0.2E+01;"}, 456 {"(RD,E8.1,';')", "-0.3E+01;"}, 457 {"(RU,E8.1,';')", "-0.2E+01;"}, 458 {"(RZ,E8.1,';')", "-0.2E+01;"}, 459 {"(RC,E8.1,';')", "-0.3E+01;"}, 460 }}, 461 {// least positive nonzero subnormal 462 1, 463 { 464 {"(E32.17,';')", " 0.49406564584124654-323;"}, 465 {"(ES32.17,';')", " 4.94065645841246544-324;"}, 466 {"(EN32.17,';')", " 4.94065645841246544-324;"}, 467 {"(E759.752,';')", 468 " 0." 469 "494065645841246544176568792868221372365059802614324764425585" 470 "682500675507270208751865299836361635992379796564695445717730" 471 "926656710355939796398774796010781878126300713190311404527845" 472 "817167848982103688718636056998730723050006387409153564984387" 473 "312473397273169615140031715385398074126238565591171026658556" 474 "686768187039560310624931945271591492455329305456544401127480" 475 "129709999541931989409080416563324524757147869014726780159355" 476 "238611550134803526493472019379026810710749170333222684475333" 477 "572083243193609238289345836806010601150616980975307834227731" 478 "832924790498252473077637592724787465608477820373446969953364" 479 "701797267771758512566055119913150489110145103786273816725095" 480 "583738973359899366480994116420570263709027924276754456522908" 481 "75386825064197182655334472656250-323;"}, 482 {"(G0,';')", ".5-323;"}, 483 {"(E757.750,';')", 484 " 0." 485 "494065645841246544176568792868221372365059802614324764425585" 486 "682500675507270208751865299836361635992379796564695445717730" 487 "926656710355939796398774796010781878126300713190311404527845" 488 "817167848982103688718636056998730723050006387409153564984387" 489 "312473397273169615140031715385398074126238565591171026658556" 490 "686768187039560310624931945271591492455329305456544401127480" 491 "129709999541931989409080416563324524757147869014726780159355" 492 "238611550134803526493472019379026810710749170333222684475333" 493 "572083243193609238289345836806010601150616980975307834227731" 494 "832924790498252473077637592724787465608477820373446969953364" 495 "701797267771758512566055119913150489110145103786273816725095" 496 "583738973359899366480994116420570263709027924276754456522908" 497 "753868250641971826553344726562-323;"}, 498 {"(RN,E757.750,';')", 499 " 0." 500 "494065645841246544176568792868221372365059802614324764425585" 501 "682500675507270208751865299836361635992379796564695445717730" 502 "926656710355939796398774796010781878126300713190311404527845" 503 "817167848982103688718636056998730723050006387409153564984387" 504 "312473397273169615140031715385398074126238565591171026658556" 505 "686768187039560310624931945271591492455329305456544401127480" 506 "129709999541931989409080416563324524757147869014726780159355" 507 "238611550134803526493472019379026810710749170333222684475333" 508 "572083243193609238289345836806010601150616980975307834227731" 509 "832924790498252473077637592724787465608477820373446969953364" 510 "701797267771758512566055119913150489110145103786273816725095" 511 "583738973359899366480994116420570263709027924276754456522908" 512 "753868250641971826553344726562-323;"}, 513 {"(RD,E757.750,';')", 514 " 0." 515 "494065645841246544176568792868221372365059802614324764425585" 516 "682500675507270208751865299836361635992379796564695445717730" 517 "926656710355939796398774796010781878126300713190311404527845" 518 "817167848982103688718636056998730723050006387409153564984387" 519 "312473397273169615140031715385398074126238565591171026658556" 520 "686768187039560310624931945271591492455329305456544401127480" 521 "129709999541931989409080416563324524757147869014726780159355" 522 "238611550134803526493472019379026810710749170333222684475333" 523 "572083243193609238289345836806010601150616980975307834227731" 524 "832924790498252473077637592724787465608477820373446969953364" 525 "701797267771758512566055119913150489110145103786273816725095" 526 "583738973359899366480994116420570263709027924276754456522908" 527 "753868250641971826553344726562-323;"}, 528 {"(RU,E757.750,';')", 529 " 0." 530 "494065645841246544176568792868221372365059802614324764425585" 531 "682500675507270208751865299836361635992379796564695445717730" 532 "926656710355939796398774796010781878126300713190311404527845" 533 "817167848982103688718636056998730723050006387409153564984387" 534 "312473397273169615140031715385398074126238565591171026658556" 535 "686768187039560310624931945271591492455329305456544401127480" 536 "129709999541931989409080416563324524757147869014726780159355" 537 "238611550134803526493472019379026810710749170333222684475333" 538 "572083243193609238289345836806010601150616980975307834227731" 539 "832924790498252473077637592724787465608477820373446969953364" 540 "701797267771758512566055119913150489110145103786273816725095" 541 "583738973359899366480994116420570263709027924276754456522908" 542 "753868250641971826553344726563-323;"}, 543 {"(RC,E757.750,';')", 544 " 0." 545 "494065645841246544176568792868221372365059802614324764425585" 546 "682500675507270208751865299836361635992379796564695445717730" 547 "926656710355939796398774796010781878126300713190311404527845" 548 "817167848982103688718636056998730723050006387409153564984387" 549 "312473397273169615140031715385398074126238565591171026658556" 550 "686768187039560310624931945271591492455329305456544401127480" 551 "129709999541931989409080416563324524757147869014726780159355" 552 "238611550134803526493472019379026810710749170333222684475333" 553 "572083243193609238289345836806010601150616980975307834227731" 554 "832924790498252473077637592724787465608477820373446969953364" 555 "701797267771758512566055119913150489110145103786273816725095" 556 "583738973359899366480994116420570263709027924276754456522908" 557 "753868250641971826553344726563-323;"}, 558 }}, 559 {// least positive nonzero normal 560 0x10000000000000, 561 { 562 {"(E723.716,';')", 563 " 0." 564 "222507385850720138309023271733240406421921598046233183055332" 565 "741688720443481391819585428315901251102056406733973103581100" 566 "515243416155346010885601238537771882113077799353200233047961" 567 "014744258363607192156504694250373420837525080665061665815894" 568 "872049117996859163964850063590877011830487479978088775374994" 569 "945158045160505091539985658247081864511353793580499211598108" 570 "576605199243335211435239014879569960959128889160299264151106" 571 "346631339366347758651302937176204732563178148566435087212282" 572 "863764204484681140761391147706280168985324411002416144742161" 573 "856716615054015428508471675290190316132277889672970737312333" 574 "408698898317506783884692609277397797285865965494109136909540" 575 "61364675687023986783152906809846172109246253967285156250-" 576 "307;"}, 577 {"(G0,';')", ".22250738585072014-307;"}, 578 }}, 579 {// greatest finite 580 0x7fefffffffffffffuLL, 581 { 582 {"(E32.17,';')", " 0.17976931348623157+309;"}, 583 {"(E317.310,';')", 584 " 0." 585 "179769313486231570814527423731704356798070567525844996598917" 586 "476803157260780028538760589558632766878171540458953514382464" 587 "234321326889464182768467546703537516986049910576551282076245" 588 "490090389328944075868508455133942304583236903222948165808559" 589 "332123348274797826204144723168738177180919299881250404026184" 590 "1248583680+309;"}, 591 {"(ES317.310,';')", 592 " 1." 593 "797693134862315708145274237317043567980705675258449965989174" 594 "768031572607800285387605895586327668781715404589535143824642" 595 "343213268894641827684675467035375169860499105765512820762454" 596 "900903893289440758685084551339423045832369032229481658085593" 597 "321233482747978262041447231687381771809192998812504040261841" 598 "2485836800+308;"}, 599 {"(EN319.310,';')", 600 " 179." 601 "769313486231570814527423731704356798070567525844996598917476" 602 "803157260780028538760589558632766878171540458953514382464234" 603 "321326889464182768467546703537516986049910576551282076245490" 604 "090389328944075868508455133942304583236903222948165808559332" 605 "123348274797826204144723168738177180919299881250404026184124" 606 "8583680000+306;"}, 607 {"(G0,';')", ".17976931348623157+309;"}, 608 }}, 609 }; 610 611 for (auto const &[value, cases] : testCases) { 612 for (auto const &[format, expect] : cases) { 613 ASSERT_TRUE(CompareFormatReal(format, value, expect)) 614 << "Failed to format " << format << ", expected " << expect; 615 } 616 } 617 618 using IndividualTestCaseTy = std::tuple<const char *, double, const char *>; 619 static const std::vector<IndividualTestCaseTy> individualTestCases{ 620 {"(F5.3,';')", 25., "*****;"}, 621 {"(F5.3,';')", 2.5, "2.500;"}, 622 {"(F5.3,';')", 0.25, "0.250;"}, 623 {"(F5.3,';')", 0.025, "0.025;"}, 624 {"(F5.3,';')", 0.0025, "0.003;"}, 625 {"(F5.3,';')", 0.00025, "0.000;"}, 626 {"(F5.3,';')", 0.000025, "0.000;"}, 627 {"(F5.3,';')", -25., "*****;"}, 628 {"(F5.3,';')", -2.5, "*****;"}, 629 {"(F5.3,';')", -0.25, "-.250;"}, 630 {"(F5.3,';')", -0.025, "-.025;"}, 631 {"(F5.3,';')", -0.0025, "-.003;"}, 632 {"(F5.3,';')", -0.00025, "-.000;"}, 633 {"(F5.3,';')", -0.000025, "-.000;"}, 634 {"(F5.3,';')", 99.999, "*****;"}, 635 {"(F5.3,';')", 9.9999, "*****;"}, 636 {"(F5.3,';')", 0.99999, "1.000;"}, 637 {"(F5.3,';')", 0.099999, "0.100;"}, 638 {"(F5.3,';')", 0.0099999, "0.010;"}, 639 {"(F5.3,';')", 0.00099999, "0.001;"}, 640 {"(F5.3,';')", 0.0005, "0.001;"}, 641 {"(F5.3,';')", 0.00049999, "0.000;"}, 642 {"(F5.3,';')", 0.000099999, "0.000;"}, 643 {"(F5.3,';')", -99.999, "*****;"}, 644 {"(F5.3,';')", -9.9999, "*****;"}, 645 {"(F5.3,';')", -0.99999, "*****;"}, 646 {"(F5.3,';')", -0.099999, "-.100;"}, 647 {"(F5.3,';')", -0.0099999, "-.010;"}, 648 {"(F5.3,';')", -0.00099999, "-.001;"}, 649 {"(F5.3,';')", -0.0005, "-.001;"}, 650 {"(F5.3,';')", -0.00049999, "-.000;"}, 651 {"(F5.3,';')", -0.000099999, "-.000;"}, 652 }; 653 654 for (auto const &[format, value, expect] : individualTestCases) { 655 ASSERT_TRUE(CompareFormatReal(format, value, expect)) 656 << "Failed to format " << format << ", expected " << expect; 657 } 658 } 659 660 //------------------------------------------------------------------------------ 661 /// Tests for input formatting real values 662 //------------------------------------------------------------------------------ 663 664 // Ensure double input values correctly map to raw uint64 values 665 TEST(IOApiTests, FormatDoubleInputValues) { 666 using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t>; 667 static const std::vector<TestCaseTy> testCases{ 668 {"(F18.0)", " 0", 0x0}, 669 {"(F18.0)", " ", 0x0}, 670 {"(F18.0)", " -0", 0x8000000000000000}, 671 {"(F18.0)", " 01", 0x3ff0000000000000}, 672 {"(F18.0)", " 1", 0x3ff0000000000000}, 673 {"(F18.0)", " 125.", 0x405f400000000000}, 674 {"(F18.0)", " 12.5", 0x4029000000000000}, 675 {"(F18.0)", " 1.25", 0x3ff4000000000000}, 676 {"(F18.0)", " 01.25", 0x3ff4000000000000}, 677 {"(F18.0)", " .125", 0x3fc0000000000000}, 678 {"(F18.0)", " 0.125", 0x3fc0000000000000}, 679 {"(F18.0)", " .0625", 0x3fb0000000000000}, 680 {"(F18.0)", " 0.0625", 0x3fb0000000000000}, 681 {"(F18.0)", " 125", 0x405f400000000000}, 682 {"(F18.1)", " 125", 0x4029000000000000}, 683 {"(F18.2)", " 125", 0x3ff4000000000000}, 684 {"(F18.3)", " 125", 0x3fc0000000000000}, 685 {"(-1P,F18.0)", " 125", 0x4093880000000000}, // 1250 686 {"(1P,F18.0)", " 125", 0x4029000000000000}, // 12.5 687 {"(BZ,F18.0)", " 125 ", 0x4093880000000000}, // 1250 688 {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13 689 {"(DC,F18.0)", " 12,5", 0x4029000000000000}, 690 }; 691 for (auto const &[format, data, want] : testCases) { 692 auto cookie{IONAME(BeginInternalFormattedInput)( 693 data, std::strlen(data), format, std::strlen(format))}; 694 union { 695 double x; 696 std::uint64_t raw; 697 } u; 698 u.raw = 0; 699 700 // Read buffer into union value 701 IONAME(EnableHandlers)(cookie, true, true, true, true, true); 702 IONAME(InputReal64)(cookie, u.x); 703 704 static constexpr int bufferSize{65}; 705 char iomsg[bufferSize]; 706 std::memset(iomsg, '\0', bufferSize - 1); 707 708 // Ensure no errors were encountered reading input buffer into union value 709 IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1); 710 auto status{IONAME(EndIoStatement)(cookie)}; 711 ASSERT_EQ(status, 0) << '\'' << format << "' failed reading '" << data 712 << "', status " << static_cast<int>(status) 713 << " iomsg '" << iomsg << "'"; 714 715 // Ensure raw uint64 value matches expected conversion from double 716 ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data 717 << "', want " << want << ", got " << u.raw; 718 } 719 } 720