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