//===-- flang/unittests/Runtime/NumericalFormatTest.cpp ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "CrashHandlerFixture.h" #include "flang/Runtime/descriptor.h" #include "flang/Runtime/io-api-consts.h" #include #include #include #include #include using namespace Fortran::runtime; using namespace Fortran::runtime::io; static bool CompareFormattedStrings( const std::string &expect, const std::string &got) { std::string want{expect}; want.resize(got.size(), ' '); return want == got; } static bool CompareFormattedStrings( const char *expect, const std::string &&got) { return CompareFormattedStrings(std::string(expect), std::move(got)); } // Perform format and compare the result with expected value static bool CompareFormatReal( const char *format, double x, const char *expect, std::string &got) { char buffer[800]; auto cookie{IONAME(BeginInternalFormattedOutput)( buffer, sizeof buffer, format, std::strlen(format))}; EXPECT_TRUE(IONAME(OutputReal64)(cookie, x)); auto status{IONAME(EndIoStatement)(cookie)}; EXPECT_EQ(status, 0); got = std::string{buffer, sizeof buffer}; auto lastNonBlank{got.find_last_not_of(" ")}; if (lastNonBlank != std::string::npos) { got.resize(lastNonBlank + 1); } return CompareFormattedStrings(expect, got); } // Convert raw uint64 into double, perform format, and compare with expected static bool CompareFormatReal(const char *format, std::uint64_t xInt, const char *expect, std::string &got) { double x; static_assert(sizeof(double) == sizeof(std::uint64_t), "Size of double != size of uint64_t!"); std::memcpy(&x, &xInt, sizeof xInt); return CompareFormatReal(format, x, expect, got); } static bool CompareFormatInteger( const char *format, std::int64_t x, const char *expect, std::string &got) { char buffer[800]; auto cookie{IONAME(BeginInternalFormattedOutput)( buffer, sizeof buffer, format, std::strlen(format))}; EXPECT_TRUE(IONAME(OutputInteger64)(cookie, x)); auto status{IONAME(EndIoStatement)(cookie)}; EXPECT_EQ(status, 0); got = std::string{buffer, sizeof buffer}; auto lastNonBlank{got.find_last_not_of(" ")}; if (lastNonBlank != std::string::npos) { got.resize(lastNonBlank + 1); } return CompareFormattedStrings(expect, got); } struct IOApiTests : CrashHandlerFixture {}; TEST(IOApiTests, HelloWorldOutputTest) { static constexpr int bufferSize{32}; char buffer[bufferSize]; // Create format for all types and values to be written const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"}; auto cookie{IONAME(BeginInternalFormattedOutput)( buffer, bufferSize, format, std::strlen(format))}; // Write string, integer, and logical values to buffer IONAME(OutputAscii)(cookie, "WORLD", 5); IONAME(OutputInteger64)(cookie, 678); IONAME(OutputInteger32)(cookie, 0xfeedface); IONAME(OutputLogical)(cookie, true); // Ensure IO succeeded auto status{IONAME(EndIoStatement)(cookie)}; ASSERT_EQ(status, 0) << "hello: '" << format << "' failed, status " << static_cast(status); // Ensure final buffer matches expected string output static const std::string expect{"HELLO, WORLD 678 0xFEEDFACE T"}; ASSERT_TRUE( CompareFormattedStrings(expect, std::string{buffer, sizeof buffer})) << "Expected '" << expect << "', got " << buffer; } TEST(IOApiTests, MultilineOutputTest) { // Allocate buffer for multiline output static constexpr int numLines{5}; static constexpr int lineLength{32}; char buffer[numLines][lineLength]; // Create descriptor for entire buffer static constexpr int staticDescriptorMaxRank{1}; StaticDescriptor wholeStaticDescriptor; Descriptor &whole{wholeStaticDescriptor.descriptor()}; static const SubscriptValue extent[]{numLines}; whole.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength, &buffer, staticDescriptorMaxRank, extent, CFI_attribute_pointer); whole.Dump(stderr); whole.Check(); // Create descriptor for buffer section StaticDescriptor sectionStaticDescriptor; Descriptor §ion{sectionStaticDescriptor.descriptor()}; static const SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1}; section.Establish(whole.type(), /*elementBytes=*/whole.ElementBytes(), nullptr, /*maxRank=*/staticDescriptorMaxRank, extent, CFI_attribute_pointer); // Ensure C descriptor address `section.raw()` is updated without error const auto error{ CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}; ASSERT_EQ(error, 0) << "multiline: CFI_section failed: " << error; section.Dump(stderr); section.Check(); // Create format string and initialize IO operation const char *format{ "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"}; auto cookie{IONAME(BeginInternalArrayFormattedOutput)( section, format, std::strlen(format))}; // Fill last line with periods std::memset(buffer[numLines - 1], '.', lineLength); // Write data to buffer IONAME(OutputAscii)(cookie, "WORLD", 5); IONAME(OutputAscii)(cookie, "HELLO", 5); IONAME(OutputInteger64)(cookie, 789); for (int j{666}; j <= 999; j += 111) { IONAME(OutputInteger64)(cookie, j); } // Ensure no errors occured in write operations above const auto status{IONAME(EndIoStatement)(cookie)}; ASSERT_EQ(status, 0) << "multiline: '" << format << "' failed, status " << static_cast(status); static const std::string expect{">HELLO, WORLD <" " " "789 abcd 666 777" " 888 999 " "................................"}; // Ensure formatted string matches expected output EXPECT_TRUE( CompareFormattedStrings(expect, std::string{buffer[0], sizeof buffer})) << "Expected '" << expect << "' but got '" << std::string{buffer[0], sizeof buffer} << "'"; } TEST(IOApiTests, ListInputTest) { static const char input[]{",1*,(5.,6.),(7.0,8.0)"}; auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)}; // Create real values for IO tests static constexpr int numRealValues{8}; float z[numRealValues]; for (int j{0}; j < numRealValues; ++j) { z[j] = -(j + 1); } // Ensure reading complex values to floats does not result in an error for (int j{0}; j < numRealValues; j += 2) { ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j])) << "InputComplex32 failed with value " << z[j]; } // Ensure no IO errors occured during IO operations above auto status{IONAME(EndIoStatement)(cookie)}; ASSERT_EQ(status, 0) << "Failed complex list-directed input, status " << static_cast(status); // Ensure writing complex values from floats does not result in an error static constexpr int bufferSize{39}; char output[bufferSize]; output[bufferSize - 1] = '\0'; cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1); for (int j{0}; j < numRealValues; j += 2) { ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1])) << "OutputComplex32 failed when outputting value " << z[j] << ", " << z[j + 1]; } // Ensure no IO errors occured during IO operations above status = IONAME(EndIoStatement)(cookie); ASSERT_EQ(status, 0) << "Failed complex list-directed output, status " << static_cast(status); // Verify output buffer against expected value static const char expect[bufferSize]{ " (-1.,-2.) (-3.,-4.) (5.,6.) (7.,8.) "}; ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0) << "Failed complex list-directed output, expected '" << expect << "', but got '" << output << "'"; } TEST(IOApiTests, DescriptorOutputTest) { static constexpr int bufferSize{10}; char buffer[bufferSize]; const char *format{"(2A4)"}; auto cookie{IONAME(BeginInternalFormattedOutput)( buffer, bufferSize, format, std::strlen(format))}; // Create descriptor for output static constexpr int staticDescriptorMaxRank{1}; StaticDescriptor staticDescriptor; Descriptor &desc{staticDescriptor.descriptor()}; static constexpr int subscriptExtent{2}; static const SubscriptValue extent[]{subscriptExtent}; // Manually write to descriptor buffer static constexpr int dataLength{4}; char data[subscriptExtent][dataLength]; std::memcpy(data[0], "ABCD", dataLength); std::memcpy(data[1], "EFGH", dataLength); desc.Establish(TypeCode{CFI_type_char}, dataLength, &data, staticDescriptorMaxRank, extent); desc.Dump(stderr); desc.Check(); IONAME(OutputDescriptor)(cookie, desc); // Ensure no errors were encountered in initializing the cookie and descriptor auto formatStatus{IONAME(EndIoStatement)(cookie)}; ASSERT_EQ(formatStatus, 0) << "descrOutputTest: '" << format << "' failed, status " << static_cast(formatStatus); // Ensure buffer matches expected output EXPECT_TRUE( CompareFormattedStrings("ABCDEFGH ", std::string{buffer, sizeof buffer})) << "descrOutputTest: formatted: got '" << std::string{buffer, sizeof buffer} << "'"; // Begin list-directed output on cookie by descriptor cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer); IONAME(OutputDescriptor)(cookie, desc); // Ensure list-directed output does not result in an IO error auto listDirectedStatus{IONAME(EndIoStatement)(cookie)}; ASSERT_EQ(listDirectedStatus, 0) << "descrOutputTest: list-directed failed, status " << static_cast(listDirectedStatus); // Ensure buffer matches expected output EXPECT_TRUE( CompareFormattedStrings(" ABCDEFGH ", std::string{buffer, sizeof buffer})) << "descrOutputTest: list-directed: got '" << std::string{buffer, sizeof buffer} << "'"; } //------------------------------------------------------------------------------ /// Tests for output formatting real values //------------------------------------------------------------------------------ TEST(IOApiTests, FormatZeroes) { static constexpr std::pair zeroes[]{ {"(E32.17,';')", " 0.00000000000000000E+00;"}, {"(F32.17,';')", " 0.00000000000000000;"}, {"(G32.17,';')", " 0.0000000000000000 ;"}, {"(DC,E32.17,';')", " 0,00000000000000000E+00;"}, {"(DC,F32.17,';')", " 0,00000000000000000;"}, {"(DC,G32.17,';')", " 0,0000000000000000 ;"}, {"(D32.17,';')", " 0.00000000000000000D+00;"}, {"(E32.17E1,';')", " 0.00000000000000000E+0;"}, {"(G32.17E1,';')", " 0.0000000000000000 ;"}, {"(E32.17E0,';')", " 0.00000000000000000E+0;"}, {"(G32.17E0,';')", " 0.0000000000000000 ;"}, {"(1P,E32.17,';')", " 0.00000000000000000E+00;"}, {"(1PE32.17,';')", " 0.00000000000000000E+00;"}, // no comma {"(1P,F32.17,';')", " 0.00000000000000000;"}, {"(1P,G32.17,';')", " 0.0000000000000000 ;"}, {"(2P,E32.17,';')", " 00.0000000000000000E+00;"}, {"(-1P,E32.17,';')", " 0.00000000000000000E+00;"}, {"(EX32.17,';')", " 0X0.00000000000000000P+0;"}, {"(DC,EX32.17,';')", " 0X0,00000000000000000P+0;"}, {"(G0,';')", "0.;"}, }; for (auto const &[format, expect] : zeroes) { std::string got; ASSERT_TRUE(CompareFormatReal(format, 0.0, expect, got)) << "Failed to format " << format << ", expected '" << expect << "', got '" << got << "'"; } } TEST(IOApiTests, FormatOnes) { static constexpr std::pair ones[]{ {"(E32.17,';')", " 0.10000000000000000E+01;"}, {"(F32.17,';')", " 1.00000000000000000;"}, {"(G32.17,';')", " 1.0000000000000000 ;"}, {"(E32.17E1,';')", " 0.10000000000000000E+1;"}, {"(G32.17E1,';')", " 1.0000000000000000 ;"}, {"(E32.17E0,';')", " 0.10000000000000000E+1;"}, {"(G32.17E0,';')", " 1.0000000000000000 ;"}, {"(E32.17E4,';')", " 0.10000000000000000E+0001;"}, {"(G32.17E4,';')", " 1.0000000000000000 ;"}, {"(1P,E32.17,';')", " 1.00000000000000000E+00;"}, {"(1PE32.17,';')", " 1.00000000000000000E+00;"}, // no comma {"(1P,F32.17,';')", " 10.00000000000000000;"}, {"(1P,G32.17,';')", " 1.0000000000000000 ;"}, {"(ES32.17,';')", " 1.00000000000000000E+00;"}, {"(2P,E32.17,';')", " 10.0000000000000000E-01;"}, {"(2P,G32.17,';')", " 1.0000000000000000 ;"}, {"(-1P,E32.17,';')", " 0.01000000000000000E+02;"}, {"(-1P,G32.17,';')", " 1.0000000000000000 ;"}, {"(EX32.17,';')", " 0X8.00000000000000000P-3;"}, {"(DC,EX32.17,';')", " 0X8,00000000000000000P-3;"}, {"(G0,';')", "1.;"}, }; for (auto const &[format, expect] : ones) { std::string got; ASSERT_TRUE(CompareFormatReal(format, 1.0, expect, got)) << "Failed to format " << format << ", expected '" << expect << "', got '" << got << "'"; } } TEST(IOApiTests, FormatNegativeOnes) { static constexpr std::tuple negOnes[]{ {"(E32.17,';')", " -0.10000000000000000E+01;"}, {"(F32.17,';')", " -1.00000000000000000;"}, {"(G32.17,';')", " -1.0000000000000000 ;"}, {"(EX32.17,';')", " -0X8.00000000000000000P-3;"}, {"(G0,';')", "-1.;"}, }; for (auto const &[format, expect] : negOnes) { std::string got; ASSERT_TRUE(CompareFormatReal(format, -1.0, expect, got)) << "Failed to format " << format << ", expected '" << expect << "', got '" << got << "'"; } } // Each test case contains a raw uint64, a format string for a real value, and // the expected resulting string from formatting the raw uint64. The double // representation of the uint64 is commented above each test case. TEST(IOApiTests, FormatDoubleValues) { using TestCaseTy = std::tuple>>; static const std::vector testCases{ {// -0 0x8000000000000000, { {"(E9.1,';')", " -0.0E+00;"}, {"(F4.0,';')", " -0.;"}, {"(F0.1,';')", "-.0;"}, {"(G8.0,';')", "-0.0E+00;"}, {"(G8.1,';')", " -0. ;"}, {"(G0,';')", "-0.;"}, {"(E9.1,';')", " -0.0E+00;"}, {"(EX9.1,';')", "-0X0.0P+0;"}, }}, {// +Inf 0x7ff0000000000000, { {"(E9.1,';')", " Inf;"}, {"(F9.1,';')", " Inf;"}, {"(G9.1,';')", " Inf;"}, {"(EX9.1,';')", " Inf;"}, {"(SP,E9.1,';')", " +Inf;"}, {"(SP,F9.1,';')", " +Inf;"}, {"(SP,G9.1,';')", " +Inf;"}, {"(SP,EX9.1,';')", " +Inf;"}, {"(G0,';')", "Inf;"}, }}, {// -Inf 0xfff0000000000000, { {"(E9.1,';')", " -Inf;"}, {"(F9.1,';')", " -Inf;"}, {"(G9.1,';')", " -Inf;"}, {"(EX9.1,';')", " -Inf;"}, {"(G0,';')", "-Inf;"}, }}, {// NaN 0x7ff0000000000001, { {"(E9.1,';')", " NaN;"}, {"(F9.1,';')", " NaN;"}, {"(G9.1,';')", " NaN;"}, {"(EX9.1,';')", " NaN;"}, {"(G0,';')", "NaN;"}, }}, {// NaN (sign irrelevant) 0xfff0000000000001, { {"(E9.1,';')", " NaN;"}, {"(F9.1,';')", " NaN;"}, {"(G9.1,';')", " NaN;"}, {"(SP,E9.1,';')", " NaN;"}, {"(SP,F9.1,';')", " NaN;"}, {"(SP,G9.1,';')", " NaN;"}, {"(SP,EX9.1,';')", " NaN;"}, {"(G0,';')", "NaN;"}, }}, {// 0.1 rounded 0x3fb999999999999a, { {"(E62.55,';')", " 0.1000000000000000055511151231257827021181583404541015625E+" "00;"}, {"(E0.1,';')", ".1E+00;"}, {"(E0.55,';')", ".1000000000000000055511151231257827021181583404541015625E+" "00;"}, {"(E0,';')", ".1E+00;"}, {"(F58.55,';')", " 0." "1000000000000000055511151231257827021181583404541015625;"}, {"(F0.0,';')", "0.;"}, {"(F0.55,';')", ".1000000000000000055511151231257827021181583404541015625;"}, {"(F0,';')", ".1;"}, {"(G62.55,';')", " 0.1000000000000000055511151231257827021181583404541015625 " " ;"}, {"(G0.0,';')", "0.;"}, {"(G0.55,';')", ".1000000000000000055511151231257827021181583404541015625;"}, {"(G0,';')", ".1;"}, {"(EX20.12,';')", " 0XC.CCCCCCCCCCCDP-7;"}, }}, {// 1.5 0x3ff8000000000000, { {"(E9.2,';')", " 0.15E+01;"}, {"(F4.1,';')", " 1.5;"}, {"(G7.1,';')", " 2. ;"}, {"(EX9.1,';')", " 0XC.0P-3;"}, {"(RN,E8.1,';')", " 0.2E+01;"}, {"(RN,F3.0,';')", " 2.;"}, {"(RN,G7.0,';')", " 0.E+01;"}, {"(RN,G7.1,';')", " 2. ;"}, {"(RD,E8.1,';')", " 0.1E+01;"}, {"(RD,F3.0,';')", " 1.;"}, {"(RD,G7.0,';')", " 0.E+01;"}, {"(RD,G7.1,';')", " 1. ;"}, {"(RU,E8.1,';')", " 0.2E+01;"}, {"(RU,G7.0,';')", " 0.E+01;"}, {"(RU,G7.1,';')", " 2. ;"}, {"(RZ,E8.1,';')", " 0.1E+01;"}, {"(RZ,F3.0,';')", " 1.;"}, {"(RZ,G7.0,';')", " 0.E+01;"}, {"(RZ,G7.1,';')", " 1. ;"}, {"(RC,E8.1,';')", " 0.2E+01;"}, {"(RC,F3.0,';')", " 2.;"}, {"(RC,G7.0,';')", " 0.E+01;"}, {"(RC,G7.1,';')", " 2. ;"}, }}, {// -1.5 0xbff8000000000000, { {"(E9.2,';')", "-0.15E+01;"}, {"(RN,E8.1,';')", "-0.2E+01;"}, {"(RD,E8.1,';')", "-0.2E+01;"}, {"(RU,E8.1,';')", "-0.1E+01;"}, {"(RZ,E8.1,';')", "-0.1E+01;"}, {"(RC,E8.1,';')", "-0.2E+01;"}, {"(EX9.1,';')", "-0XC.0P-3;"}, }}, {// 2.5 0x4004000000000000, { {"(E9.2,';')", " 0.25E+01;"}, {"(RN,E8.1,';')", " 0.2E+01;"}, {"(RD,E8.1,';')", " 0.2E+01;"}, {"(RU,E8.1,';')", " 0.3E+01;"}, {"(RZ,E8.1,';')", " 0.2E+01;"}, {"(RC,E8.1,';')", " 0.3E+01;"}, {"(EX9.1,';')", " 0XA.0P-2;"}, }}, {// -2.5 0xc004000000000000, { {"(E9.2,';')", "-0.25E+01;"}, {"(RN,E8.1,';')", "-0.2E+01;"}, {"(RD,E8.1,';')", "-0.3E+01;"}, {"(RU,E8.1,';')", "-0.2E+01;"}, {"(RZ,E8.1,';')", "-0.2E+01;"}, {"(RC,E8.1,';')", "-0.3E+01;"}, {"(EX9.1,';')", "-0XA.0P-2;"}, }}, {// least positive nonzero subnormal 1, { {"(E32.17,';')", " 0.49406564584124654-323;"}, {"(ES32.17,';')", " 4.94065645841246544-324;"}, {"(EN32.17,';')", " 4.94065645841246544-324;"}, {"(E759.752,';')", " 0." "494065645841246544176568792868221372365059802614324764425585" "682500675507270208751865299836361635992379796564695445717730" "926656710355939796398774796010781878126300713190311404527845" "817167848982103688718636056998730723050006387409153564984387" "312473397273169615140031715385398074126238565591171026658556" "686768187039560310624931945271591492455329305456544401127480" "129709999541931989409080416563324524757147869014726780159355" "238611550134803526493472019379026810710749170333222684475333" "572083243193609238289345836806010601150616980975307834227731" "832924790498252473077637592724787465608477820373446969953364" "701797267771758512566055119913150489110145103786273816725095" "583738973359899366480994116420570263709027924276754456522908" "75386825064197182655334472656250-323;"}, {"(G0,';')", ".5E-323;"}, {"(E757.750,';')", " 0." "494065645841246544176568792868221372365059802614324764425585" "682500675507270208751865299836361635992379796564695445717730" "926656710355939796398774796010781878126300713190311404527845" "817167848982103688718636056998730723050006387409153564984387" "312473397273169615140031715385398074126238565591171026658556" "686768187039560310624931945271591492455329305456544401127480" "129709999541931989409080416563324524757147869014726780159355" "238611550134803526493472019379026810710749170333222684475333" "572083243193609238289345836806010601150616980975307834227731" "832924790498252473077637592724787465608477820373446969953364" "701797267771758512566055119913150489110145103786273816725095" "583738973359899366480994116420570263709027924276754456522908" "753868250641971826553344726562-323;"}, {"(RN,E757.750,';')", " 0." "494065645841246544176568792868221372365059802614324764425585" "682500675507270208751865299836361635992379796564695445717730" "926656710355939796398774796010781878126300713190311404527845" "817167848982103688718636056998730723050006387409153564984387" "312473397273169615140031715385398074126238565591171026658556" "686768187039560310624931945271591492455329305456544401127480" "129709999541931989409080416563324524757147869014726780159355" "238611550134803526493472019379026810710749170333222684475333" "572083243193609238289345836806010601150616980975307834227731" "832924790498252473077637592724787465608477820373446969953364" "701797267771758512566055119913150489110145103786273816725095" "583738973359899366480994116420570263709027924276754456522908" "753868250641971826553344726562-323;"}, {"(RD,E757.750,';')", " 0." "494065645841246544176568792868221372365059802614324764425585" "682500675507270208751865299836361635992379796564695445717730" "926656710355939796398774796010781878126300713190311404527845" "817167848982103688718636056998730723050006387409153564984387" "312473397273169615140031715385398074126238565591171026658556" "686768187039560310624931945271591492455329305456544401127480" "129709999541931989409080416563324524757147869014726780159355" "238611550134803526493472019379026810710749170333222684475333" "572083243193609238289345836806010601150616980975307834227731" "832924790498252473077637592724787465608477820373446969953364" "701797267771758512566055119913150489110145103786273816725095" "583738973359899366480994116420570263709027924276754456522908" "753868250641971826553344726562-323;"}, {"(RU,E757.750,';')", " 0." "494065645841246544176568792868221372365059802614324764425585" "682500675507270208751865299836361635992379796564695445717730" "926656710355939796398774796010781878126300713190311404527845" "817167848982103688718636056998730723050006387409153564984387" "312473397273169615140031715385398074126238565591171026658556" "686768187039560310624931945271591492455329305456544401127480" "129709999541931989409080416563324524757147869014726780159355" "238611550134803526493472019379026810710749170333222684475333" "572083243193609238289345836806010601150616980975307834227731" "832924790498252473077637592724787465608477820373446969953364" "701797267771758512566055119913150489110145103786273816725095" "583738973359899366480994116420570263709027924276754456522908" "753868250641971826553344726563-323;"}, {"(RC,E757.750,';')", " 0." "494065645841246544176568792868221372365059802614324764425585" "682500675507270208751865299836361635992379796564695445717730" "926656710355939796398774796010781878126300713190311404527845" "817167848982103688718636056998730723050006387409153564984387" "312473397273169615140031715385398074126238565591171026658556" "686768187039560310624931945271591492455329305456544401127480" "129709999541931989409080416563324524757147869014726780159355" "238611550134803526493472019379026810710749170333222684475333" "572083243193609238289345836806010601150616980975307834227731" "832924790498252473077637592724787465608477820373446969953364" "701797267771758512566055119913150489110145103786273816725095" "583738973359899366480994116420570263709027924276754456522908" "753868250641971826553344726563-323;"}, {"(EX24.13,';')", " 0X8.0000000000000P-1077;"}, }}, {// least positive nonzero normal 0x10000000000000, { {"(E723.716,';')", " 0." "222507385850720138309023271733240406421921598046233183055332" "741688720443481391819585428315901251102056406733973103581100" "515243416155346010885601238537771882113077799353200233047961" "014744258363607192156504694250373420837525080665061665815894" "872049117996859163964850063590877011830487479978088775374994" "945158045160505091539985658247081864511353793580499211598108" "576605199243335211435239014879569960959128889160299264151106" "346631339366347758651302937176204732563178148566435087212282" "863764204484681140761391147706280168985324411002416144742161" "856716615054015428508471675290190316132277889672970737312333" "408698898317506783884692609277397797285865965494109136909540" "61364675687023986783152906809846172109246253967285156250-" "307;"}, {"(G0,';')", ".22250738585072014E-307;"}, {"(EX24.13,';')", " 0X8.0000000000000P-1025;"}, }}, {// greatest finite 0x7fefffffffffffffuLL, { {"(E32.17,';')", " 0.17976931348623157+309;"}, {"(E317.310,';')", " 0." "179769313486231570814527423731704356798070567525844996598917" "476803157260780028538760589558632766878171540458953514382464" "234321326889464182768467546703537516986049910576551282076245" "490090389328944075868508455133942304583236903222948165808559" "332123348274797826204144723168738177180919299881250404026184" "1248583680+309;"}, {"(ES317.310,';')", " 1." "797693134862315708145274237317043567980705675258449965989174" "768031572607800285387605895586327668781715404589535143824642" "343213268894641827684675467035375169860499105765512820762454" "900903893289440758685084551339423045832369032229481658085593" "321233482747978262041447231687381771809192998812504040261841" "2485836800+308;"}, {"(EN319.310,';')", " 179." "769313486231570814527423731704356798070567525844996598917476" "803157260780028538760589558632766878171540458953514382464234" "321326889464182768467546703537516986049910576551282076245490" "090389328944075868508455133942304583236903222948165808559332" "123348274797826204144723168738177180919299881250404026184124" "8583680000+306;"}, {"(G0,';')", ".17976931348623157E+309;"}, {"(EX24.13,';')", " 0XF.FFFFFFFFFFFF8P+1020;"}, }}, {// EX rounding 0x3ff0100000000000uLL, { {"(F11.8,';')", " 1.00390625;"}, {"(EX10.2,';')", " 0X8.08P-3;"}, {"(EX10.1,';')", " 0X8.0P-3;"}, {"(EX10.0,';')", " 0X8.08P-3;"}, {"(EX0.0,';')", "0X8.08P-3;"}, {"(EX0,';')", "0X8.08P-3;"}, {"(RN,EX10.1,';')", " 0X8.0P-3;"}, {"(RU,EX10.1,';')", " 0X8.1P-3;"}, {"(RD,EX10.1,';')", " 0X8.0P-3;"}, {"(RZ,EX10.1,';')", " 0X8.0P-3;"}, {"(RC,EX10.1,';')", " 0X8.1P-3;"}, {"(RN,EX10.0,';')", " 0X8.08P-3;"}, {"(RU,EX10.0,';')", " 0X8.08P-3;"}, {"(RD,EX10.0,';')", " 0X8.08P-3;"}, {"(RZ,EX10.0,';')", " 0X8.08P-3;"}, {"(RC,EX10.0,';')", " 0X8.08P-3;"}, }}, {// EX rounding 0xbff0100000000000uLL, { {"(F11.8,';')", "-1.00390625;"}, {"(EX10.2,';')", "-0X8.08P-3;"}, {"(EX10.1,';')", " -0X8.0P-3;"}, {"(EX10.0,';')", "-0X8.08P-3;"}, {"(EX0.0,';')", "-0X8.08P-3;"}, {"(EX0,';')", "-0X8.08P-3;"}, {"(RN,EX10.1,';')", " -0X8.0P-3;"}, {"(RU,EX10.1,';')", " -0X8.0P-3;"}, {"(RD,EX10.1,';')", " -0X8.1P-3;"}, {"(RZ,EX10.1,';')", " -0X8.0P-3;"}, {"(RC,EX10.1,';')", " -0X8.1P-3;"}, {"(RN,EX10.0,';')", "-0X8.08P-3;"}, {"(RU,EX10.0,';')", "-0X8.08P-3;"}, {"(RD,EX10.0,';')", "-0X8.08P-3;"}, {"(RZ,EX10.0,';')", "-0X8.08P-3;"}, {"(RC,EX10.0,';')", "-0X8.08P-3;"}, }}, }; for (auto const &[value, cases] : testCases) { for (auto const &[format, expect] : cases) { std::string got; ASSERT_TRUE(CompareFormatReal(format, value, expect, got)) << "Failed to format " << format << ", expected '" << expect << "', got '" << got << "'"; } } using IndividualTestCaseTy = std::tuple; static const std::vector individualTestCases{ {"(F5.3,';')", 25., "*****;"}, {"(F5.3,';')", 2.5, "2.500;"}, {"(F5.3,';')", 0.25, "0.250;"}, {"(F5.3,';')", 0.025, "0.025;"}, {"(F5.3,';')", 0.0025, "0.003;"}, {"(F5.3,';')", 0.00025, "0.000;"}, {"(F5.3,';')", 0.000025, "0.000;"}, {"(F5.3,';')", -25., "*****;"}, {"(F5.3,';')", -2.5, "*****;"}, {"(F5.3,';')", -0.25, "-.250;"}, {"(F5.3,';')", -0.025, "-.025;"}, {"(F5.3,';')", -0.0025, "-.003;"}, {"(F5.3,';')", -0.00025, "-.000;"}, {"(F5.3,';')", -0.000025, "-.000;"}, {"(F5.3,';')", 99.999, "*****;"}, {"(F5.3,';')", 9.9999, "*****;"}, {"(F5.3,';')", 0.99999, "1.000;"}, {"(F5.3,';')", 0.099999, "0.100;"}, {"(F5.3,';')", 0.0099999, "0.010;"}, {"(F5.3,';')", 0.00099999, "0.001;"}, {"(F5.3,';')", 0.0005000000000000000104083408558608425664715468883514404296875, "0.001;"}, {"(F5.3,';')", 0.000499999999999999901988123607310399165726266801357269287109375, "0.000;"}, {"(F5.3,';')", 0.000099999, "0.000;"}, {"(F5.3,';')", -99.999, "*****;"}, {"(F5.3,';')", -9.9999, "*****;"}, {"(F5.3,';')", -0.99999, "*****;"}, {"(F5.3,';')", -0.099999, "-.100;"}, {"(F5.3,';')", -0.0099999, "-.010;"}, {"(F5.3,';')", -0.00099999, "-.001;"}, {"(F5.3,';')", -0.0005000000000000000104083408558608425664715468883514404296875, "-.001;"}, {"(F5.3,';')", -0.000499999999999999901988123607310399165726266801357269287109375, "-.000;"}, {"(F5.3,';')", -0.000099999, "-.000;"}, {"(F0.1,';')", 0.0, ".0;"}, {"(F5.0,';')", -0.5000000000000001, " -1.;"}, {"(F5.0,';')", -0.5, " -0.;"}, {"(F5.0,';')", -0.49999999999999994, " -0.;"}, {"(F5.0,';')", 0.49999999999999994, " 0.;"}, {"(F5.0,';')", 0.5, " 0.;"}, {"(F5.0,';')", 0.5000000000000001, " 1.;"}, {"(ES0.0E0,';')", 0.666, "7.E-1;"}, {"(EN0.0E0,';')", 0.666, "666.E-3;"}, }; for (auto const &[format, value, expect] : individualTestCases) { std::string got; char hex[17]; std::snprintf(hex, sizeof hex, "%016llx", *reinterpret_cast(&value)); ASSERT_TRUE(CompareFormatReal(format, value, expect, got)) << "Failed to format " << value << " 0x" << hex << " with format " << format << ", expected '" << expect << "', got '" << got << "'"; } // Problematic EN formatting edge cases with rounding using IndividualENTestCaseTy = std::tuple; static const std::vector individualENTestCases{ {0x3E11183197785F8C, " 995.0E-12"}, // 0.9950312500000000115852E-09 {0x3E11180E68455D30, " 995.0E-12"}, // 0.9949999999999999761502E-09 {0x3E112BD8F4F6B0D7, " 999.5E-12"}, // 0.9994999999999999089118E-09 {0x3E45794883CA8782, " 10.0E-09"}, // 0.9999499999999999642266E-08 {0x3F506218230C7482, " 999.9E-06"}, // 0.9999499999999998840761E-03 {0x3FB99652BD3C3612, " 100.0E-03"}, // 0.9999500000000000055067E+00 {0x4023E66666666667, " 10.0E+00"}, // 0.9950000000000001065814E+01 }; for (auto const &[value, expect] : individualENTestCases) { std::string got; ASSERT_TRUE(CompareFormatReal("(EN10.1)", value, expect, got)) << "Failed to format EN10.1, expected '" << expect << "', got '" << got << "'"; } } TEST(IOApiTests, FormatIntegerValues) { using IntTestCaseTy = std::tuple; static const std::vector intTestCases{ {"(I4)", 0, " 0"}, {"(I4)", 1, " 1"}, {"(I4)", 9999, "9999"}, {"(SP,I4)", 1, " +1"}, {"(SP,I4)", 9999, "****"}, {"(SP,I4)", 999, "+999"}, {"(I4)", -1, " -1"}, {"(I4)", -9999, "****"}, {"(I4)", -999, "-999"}, {"(I4.2)", 1, " 01"}, {"(I4.2)", -1, " -01"}, {"(I4.2)", 999, " 999"}, {"(I4.4)", 999, "0999"}, {"(I0)", 0, "0"}, {"(I0)", 1, "1"}, {"(I0)", 9999, "9999"}, {"(SP,I0)", 1, "+1"}, {"(SP,I0)", 9999, "+9999"}, {"(SP,I0)", 999, "+999"}, {"(I0)", -1, "-1"}, {"(I0)", -9999, "-9999"}, {"(I0)", -999, "-999"}, {"(I0.2)", 1, "01"}, {"(I0.2)", -1, "-01"}, {"(I0.2)", 999, "999"}, {"(I0.4)", 999, "0999"}, {"(G4)", 0, " 0"}, {"(G4)", 1, " 1"}, {"(G4)", 9999, "9999"}, {"(SP,G4)", 1, " +1"}, {"(SP,G4)", 9999, "****"}, {"(SP,G4)", 999, "+999"}, {"(G4)", -1, " -1"}, {"(G4)", -9999, "****"}, {"(G4)", -999, "-999"}, {"(G4.2)", 1, " 1"}, {"(G4.2)", -1, " -1"}, {"(G4.2)", 999, " 999"}, {"(G4.4)", 999, " 999"}, {"(G0)", 0, "0"}, {"(G0)", 1, "1"}, {"(G0)", 9999, "9999"}, {"(SP,G0)", 1, "+1"}, {"(SP,G0)", 9999, "+9999"}, {"(SP,G0)", 999, "+999"}, {"(G0)", -1, "-1"}, {"(G0)", -9999, "-9999"}, {"(G0)", -999, "-999"}, {"(G0.2)", 1, "1"}, {"(G0.2)", -1, "-1"}, {"(G0.2)", 999, "999"}, {"(G0.4)", 999, "999"}, }; for (auto const &[fmt, value, expect] : intTestCases) { std::string got; ASSERT_TRUE(CompareFormatInteger(fmt, value, expect, got)) << "Failed to format " << fmt << ", expected '" << expect << "', got '" << got << "'"; } } //------------------------------------------------------------------------------ /// Tests for input editing real values //------------------------------------------------------------------------------ // Ensure double input values correctly map to raw uint64 values TEST(IOApiTests, EditDoubleInputValues) { using TestCaseTy = std::tuple; int ovf{IostatRealInputOverflow}; static const std::vector testCases{ {"(F18.0)", " 0", 0x0, 0}, {"(F18.0)", " ", 0x0, 0}, {"(F18.0)", " -0", 0x8000000000000000, 0}, {"(F18.0)", " 01", 0x3ff0000000000000, 0}, {"(F18.0)", " 1", 0x3ff0000000000000, 0}, {"(F18.0)", " 125.", 0x405f400000000000, 0}, {"(F18.0)", " 12.5", 0x4029000000000000, 0}, {"(F18.0)", " 1.25", 0x3ff4000000000000, 0}, {"(F18.0)", " 01.25", 0x3ff4000000000000, 0}, {"(F18.0)", " .125", 0x3fc0000000000000, 0}, {"(F18.0)", " 0.125", 0x3fc0000000000000, 0}, {"(F18.0)", " .0625", 0x3fb0000000000000, 0}, {"(F18.0)", " 0.0625", 0x3fb0000000000000, 0}, {"(F18.0)", " 125", 0x405f400000000000, 0}, {"(F18.1)", " 125", 0x4029000000000000, 0}, {"(F18.2)", " 125", 0x3ff4000000000000, 0}, {"(F18.3)", " 125", 0x3fc0000000000000, 0}, {"(-1P,F18.0)", " 125", 0x4093880000000000, 0}, // 1250 {"(1P,F18.0)", " 125", 0x4029000000000000, 0}, // 12.5 {"(BZ,F18.0)", " 125 ", 0x4093880000000000, 0}, // 1250 {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000, 0}, // 1.25e13 {"(BZ,F18.0)", " . ", 0x0, 0}, {"(BZ,F18.0)", " . e +1 ", 0x0, 0}, {"(DC,F18.0)", " 12,5", 0x4029000000000000, 0}, {"(EX22.0)", "0X0P0 ", 0x0, 0}, // +0. {"(EX22.0)", "-0X0P0 ", 0x8000000000000000, 0}, // -0. {"(EX22.0)", "0X.8P1 ", 0x3ff0000000000000, 0}, // 1.0 {"(EX22.0)", "0X8.P-3 ", 0x3ff0000000000000, 0}, // 1.0 {"(EX22.0)", "0X.1P4 ", 0x3ff0000000000000, 0}, // 1.0 {"(EX22.0)", "0X10.P-4 ", 0x3ff0000000000000, 0}, // 1.0 {"(EX22.0)", "0X8.00P-3 ", 0x3ff0000000000000, 0}, // 1.0 {"(EX22.0)", "0X80.0P-6 ", 0x4000000000000000, 0}, // 2.0 {"(EX22.0)", "0XC.CCCCCCCCCCCDP-7 ", 0x3fb999999999999a, 0}, // 0.1 {"(EX22.0)", "0X.8P-1021 ", 0x0010000000000000, 0}, // min normal {"(EX22.0)", "0X.8P-1022 ", 0x0008000000000000, 0}, // subnormal {"(EX22.0)", "0X.8P-1073 ", 0x0000000000000001, 0}, // min subn. {"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff, 0}, // max finite {"(EX22.0)", "0X.8P1025 ", 0x7ff0000000000000, ovf}, // +Inf {"(EX22.0)", "-0X.8P1025 ", 0xfff0000000000000, ovf}, // -Inf {"(RC,EX22.0)", "0X1.0000000000000P0 ", 0x3ff0000000000000, 0}, {"(RC,EX22.0)", "0X1.00000000000008P0 ", 0x3ff0000000000001, 0}, {"(RC,EX22.0)", "0X1.000000000000008P0 ", 0x3ff0000000000000, 0}, {"(RC,EX22.0)", "0X1.00000000000004P0 ", 0x3ff0000000000000, 0}, {"(RC,EX22.0)", "0X.80000000000000P1 ", 0x3ff0000000000000, 0}, {"(RC,EX22.0)", "0X.80000000000004P1 ", 0x3ff0000000000001, 0}, {"(RC,EX22.0)", "0X.800000000000004P1 ", 0x3ff0000000000000, 0}, {"(RC,EX22.0)", "0X.80000000000002P1 ", 0x3ff0000000000000, 0}, {"(RZ,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE() {"(RD,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE() {"(RU,F7.0)", " 2.e308", 0x7ff0000000000000, ovf}, // +Inf {"(RZ,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE() {"(RD,F7.0)", "-2.e308", 0xfff0000000000000, ovf}, // -Inf {"(RU,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE() {"(RZ,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE() {"(RD,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE() {"(RU,F7.0)", " 1.e999", 0x7ff0000000000000, ovf}, // +Inf {"(RZ,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE() {"(RD,F7.0)", "-1.e999", 0xfff0000000000000, ovf}, // -Inf {"(RU,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE() {"(E9.1)", " 1.0E-325", 0x0, 0}, {"(RU,E9.1)", " 1.0E-325", 0x1, 0}, {"(E9.1)", "-1.0E-325", 0x8000000000000000, 0}, {"(RD,E9.1)", "-1.0E-325", 0x8000000000000001, 0}, }; for (auto const &[format, data, want, iostat] : testCases) { auto cookie{IONAME(BeginInternalFormattedInput)( data, std::strlen(data), format, std::strlen(format))}; union { double x; std::uint64_t raw; } u; u.raw = 0; // Read buffer into union value IONAME(EnableHandlers)(cookie, true, true, true, true, true); IONAME(InputReal64)(cookie, u.x); static constexpr int bufferSize{65}; char iomsg[bufferSize]; std::memset(iomsg, '\0', bufferSize - 1); // Ensure no unexpected errors were encountered reading input buffer into // union value IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1); auto status{IONAME(EndIoStatement)(cookie)}; ASSERT_EQ(status, iostat) << '\'' << format << "' failed reading '" << data << "', status " << static_cast(status) << " != expected " << iostat << " iomsg '" << iomsg << "'"; // Ensure raw uint64 value matches expected conversion from double ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data << "', want " << want << ", got " << u.raw; } } // regression test for confusing digit minimization TEST(IOApiTests, ConfusingMinimization) { char buffer[8]{}; auto cookie{IONAME(BeginInternalListOutput)(buffer, sizeof buffer)}; StaticDescriptor<0> staticDescriptor; Descriptor &desc{staticDescriptor.descriptor()}; std::uint16_t x{0x7bff}; // HUGE(0._2) desc.Establish(TypeCode{CFI_type_half_float}, sizeof x, &x, 0, nullptr); desc.Check(); EXPECT_TRUE(IONAME(OutputDescriptor)(cookie, desc)); auto status{IONAME(EndIoStatement)(cookie)}; EXPECT_EQ(status, 0); std::string got{std::string{buffer, sizeof buffer}}; EXPECT_TRUE(CompareFormattedStrings(" 65504. ", got)) << "expected ' 65504. ', got '" << got << '\''; // not 65500.! }