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