diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -23,11 +23,6 @@ FortranRuntime ) -add_flang_nongtest_unittest(hello - RuntimeTesting - FortranRuntime -) - # This test is not run by default as it requires input. add_executable(external-hello-world external-hello.cpp diff --git a/flang/unittests/Runtime/hello.cpp b/flang/unittests/Runtime/hello.cpp deleted file mode 100644 --- a/flang/unittests/Runtime/hello.cpp +++ /dev/null @@ -1,526 +0,0 @@ -// Basic sanity tests of I/O API; exhaustive testing will be done in Fortran - -#include "testing.h" -#include "../../runtime/descriptor.h" -#include "../../runtime/io-api.h" -#include - -using namespace Fortran::runtime; -using namespace Fortran::runtime::io; - -static bool test(const char *format, const char *expect, std::string &&got) { - std::string want{expect}; - want.resize(got.length(), ' '); - if (got != want) { - Fail() << '\'' << format << "' failed;\n got '" << got - << "',\nexpected '" << want << "'\n"; - return false; - } - return true; -} - -static void hello() { - char buffer[32]; - const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"}; - auto cookie{IONAME(BeginInternalFormattedOutput)( - buffer, sizeof buffer, format, std::strlen(format))}; - IONAME(OutputAscii)(cookie, "WORLD", 5); - IONAME(OutputInteger64)(cookie, 678); - IONAME(OutputInteger64)(cookie, 0xfeedface); - IONAME(OutputLogical)(cookie, true); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "hello: '" << format << "' failed, status " - << static_cast(status) << '\n'; - } else { - test(format, "HELLO, WORLD 678 0xFEEDFACE T", - std::string{buffer, sizeof buffer}); - } -} - -static void multiline() { - char buffer[5][32]; - StaticDescriptor<1> staticDescriptor[2]; - Descriptor &whole{staticDescriptor[0].descriptor()}; - SubscriptValue extent[]{5}; - whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent, - CFI_attribute_pointer); - whole.Dump(); - whole.Check(); - Descriptor §ion{staticDescriptor[1].descriptor()}; - SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1}; - section.Establish(whole.type(), whole.ElementBytes(), nullptr, 1, extent, - CFI_attribute_pointer); - if (auto error{ - CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}) { - Fail() << "multiline: CFI_section failed: " << error << '\n'; - return; - } - section.Dump(); - section.Check(); - const char *format{ - "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"}; - auto cookie{IONAME(BeginInternalArrayFormattedOutput)( - section, format, std::strlen(format))}; - 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); - } - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "multiline: '" << format << "' failed, status " - << static_cast(status) << '\n'; - } else { - test(format, - ">HELLO, WORLD <" - " " - "789 abcd 666 777" - " 888 999 " - " ", - std::string{buffer[0], sizeof buffer}); - } -} - -static void listInputTest() { - static const char input[]{",1*,(5.,6..)"}; - auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)}; - float z[6]; - for (int j{0}; j < 6; ++j) { - z[j] = -(j + 1); - } - for (int j{0}; j < 6; j += 2) { - if (!IONAME(InputComplex32)(cookie, &z[j])) { - Fail() << "InputComplex32 failed\n"; - } - } - auto status{IONAME(EndIoStatement)(cookie)}; - if (status) { - Fail() << "Failed complex list-directed input, status " - << static_cast(status) << '\n'; - } else { - char output[33]; - output[32] = '\0'; - cookie = IONAME(BeginInternalListOutput)(output, 32); - for (int j{0}; j < 6; j += 2) { - if (!IONAME(OutputComplex32)(cookie, z[j], z[j + 1])) { - Fail() << "OutputComplex32 failed\n"; - } - } - status = IONAME(EndIoStatement)(cookie); - static const char expect[33]{" (-1.,-2.) (-3.,-4.) (5.,6.) "}; - if (status) { - Fail() << "Failed complex list-directed output, status " - << static_cast(status) << '\n'; - } else if (std::strncmp(output, expect, 33) != 0) { - Fail() << "Failed complex list-directed output, expected '" << expect - << "', but got '" << output << "'\n"; - } - } -} - -static void descrOutputTest() { - char buffer[9]; - // Formatted - const char *format{"(2A4)"}; - auto cookie{IONAME(BeginInternalFormattedOutput)( - buffer, sizeof buffer, format, std::strlen(format))}; - StaticDescriptor<1> staticDescriptor; - Descriptor &desc{staticDescriptor.descriptor()}; - SubscriptValue extent[]{2}; - char data[2][4]; - std::memcpy(data[0], "ABCD", 4); - std::memcpy(data[1], "EFGH", 4); - desc.Establish(TypeCode{CFI_type_char}, sizeof data[0], &data, 1, extent); - desc.Dump(); - desc.Check(); - IONAME(OutputDescriptor)(cookie, desc); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "descrOutputTest: '" << format << "' failed, status " - << static_cast(status) << '\n'; - } else { - test("descrOutputTest(formatted)", "ABCDEFGH ", - std::string{buffer, sizeof buffer}); - } - // List-directed - cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer); - IONAME(OutputDescriptor)(cookie, desc); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "descrOutputTest: list-directed failed, status " - << static_cast(status) << '\n'; - } else { - test("descrOutputTest(list)", " ABCDEFGH", - std::string{buffer, sizeof buffer}); - } -} - -static void realTest(const char *format, double x, const char *expect) { - char buffer[800]; - auto cookie{IONAME(BeginInternalFormattedOutput)( - buffer, sizeof buffer, format, std::strlen(format))}; - IONAME(OutputReal64)(cookie, x); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << '\'' << format << "' failed, status " << static_cast(status) - << '\n'; - } else { - test(format, expect, std::string{buffer, sizeof buffer}); - } -} - -static void realInTest( - const char *format, const char *data, std::uint64_t want) { - auto cookie{IONAME(BeginInternalFormattedInput)( - data, std::strlen(data), format, std::strlen(format))}; - union { - double x; - std::uint64_t raw; - } u; - u.raw = 0; - IONAME(EnableHandlers)(cookie, true, true, true, true, true); - IONAME(InputReal64)(cookie, u.x); - char iomsg[65]; - iomsg[0] = '\0'; - iomsg[sizeof iomsg - 1] = '\0'; - IONAME(GetIoMsg)(cookie, iomsg, sizeof iomsg - 1); - auto status{IONAME(EndIoStatement)(cookie)}; - if (status) { - Fail() << '\'' << format << "' failed reading '" << data << "', status " - << static_cast(status) << " iomsg '" << iomsg << "'\n"; - } else if (u.raw != want) { - Fail() << '\'' << format << "' failed reading '" << data << "', want 0x"; - Fail().write_hex(want) << ", got 0x" << u.raw << '\n'; - } -} - -int main() { - StartTests(); - - hello(); - multiline(); - - static const char *zeroes[][2]{ - {"(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;"}, - {"(G0,';')", "0.;"}, {}}; - for (int j{0}; zeroes[j][0]; ++j) { - realTest(zeroes[j][0], 0.0, zeroes[j][1]); - } - - static const char *ones[][2]{ - {"(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 ;"}, - {"(G0,';')", "1.;"}, {}}; - for (int j{0}; ones[j][0]; ++j) { - realTest(ones[j][0], 1.0, ones[j][1]); - } - - realTest("(E32.17,';')", -1.0, " -0.10000000000000000E+01;"); - realTest("(F32.17,';')", -1.0, " -1.00000000000000000;"); - realTest("(G32.17,';')", -1.0, " -1.0000000000000000 ;"); - realTest("(G0,';')", -1.0, "-1.;"); - - volatile union { - double d; - std::uint64_t n; - } u; - u.n = 0x8000000000000000; // -0 - realTest("(E9.1,';')", u.d, " -0.0E+00;"); - realTest("(F4.0,';')", u.d, " -0.;"); - realTest("(G8.0,';')", u.d, "-0.0E+00;"); - realTest("(G8.1,';')", u.d, " -0. ;"); - realTest("(G0,';')", u.d, "-0.;"); - u.n = 0x7ff0000000000000; // +Inf - realTest("(E9.1,';')", u.d, " Inf;"); - realTest("(F9.1,';')", u.d, " Inf;"); - realTest("(G9.1,';')", u.d, " Inf;"); - realTest("(SP,E9.1,';')", u.d, " +Inf;"); - realTest("(SP,F9.1,';')", u.d, " +Inf;"); - realTest("(SP,G9.1,';')", u.d, " +Inf;"); - realTest("(G0,';')", u.d, "Inf;"); - u.n = 0xfff0000000000000; // -Inf - realTest("(E9.1,';')", u.d, " -Inf;"); - realTest("(F9.1,';')", u.d, " -Inf;"); - realTest("(G9.1,';')", u.d, " -Inf;"); - realTest("(G0,';')", u.d, "-Inf;"); - u.n = 0x7ff0000000000001; // NaN - realTest("(E9.1,';')", u.d, " NaN;"); - realTest("(F9.1,';')", u.d, " NaN;"); - realTest("(G9.1,';')", u.d, " NaN;"); - realTest("(G0,';')", u.d, "NaN;"); - u.n = 0xfff0000000000001; // NaN (sign irrelevant) - realTest("(E9.1,';')", u.d, " NaN;"); - realTest("(F9.1,';')", u.d, " NaN;"); - realTest("(G9.1,';')", u.d, " NaN;"); - realTest("(SP,E9.1,';')", u.d, " NaN;"); - realTest("(SP,F9.1,';')", u.d, " NaN;"); - realTest("(SP,G9.1,';')", u.d, " NaN;"); - realTest("(G0,';')", u.d, "NaN;"); - - u.n = 0x3fb999999999999a; // 0.1 rounded - realTest("(E62.55,';')", u.d, - " 0.1000000000000000055511151231257827021181583404541015625E+00;"); - realTest("(E0.0,';')", u.d, "0.E+00;"); - realTest("(E0.55,';')", u.d, - "0.1000000000000000055511151231257827021181583404541015625E+00;"); - realTest("(E0,';')", u.d, ".1E+00;"); - realTest("(F58.55,';')", u.d, - " 0.1000000000000000055511151231257827021181583404541015625;"); - realTest("(F0.0,';')", u.d, "0.;"); - realTest("(F0.55,';')", u.d, - ".1000000000000000055511151231257827021181583404541015625;"); - realTest("(F0,';')", u.d, ".1;"); - realTest("(G62.55,';')", u.d, - " 0.1000000000000000055511151231257827021181583404541015625 ;"); - realTest("(G0.0,';')", u.d, "0.;"); - realTest("(G0.55,';')", u.d, - ".1000000000000000055511151231257827021181583404541015625;"); - realTest("(G0,';')", u.d, ".1;"); - - u.n = 0x3ff8000000000000; // 1.5 - realTest("(E9.2,';')", u.d, " 0.15E+01;"); - realTest("(F4.1,';')", u.d, " 1.5;"); - realTest("(G7.1,';')", u.d, " 2. ;"); - realTest("(RN,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RN,F3.0,';')", u.d, " 2.;"); - realTest("(RN,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RN,G7.1,';')", u.d, " 2. ;"); - realTest("(RD,E8.1,';')", u.d, " 0.1E+01;"); - realTest("(RD,F3.0,';')", u.d, " 1.;"); - realTest("(RD,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RD,G7.1,';')", u.d, " 1. ;"); - realTest("(RU,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RU,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RU,G7.1,';')", u.d, " 2. ;"); - realTest("(RZ,E8.1,';')", u.d, " 0.1E+01;"); - realTest("(RZ,F3.0,';')", u.d, " 1.;"); - realTest("(RZ,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RZ,G7.1,';')", u.d, " 1. ;"); - realTest("(RC,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RC,F3.0,';')", u.d, " 2.;"); - realTest("(RC,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RC,G7.1,';')", u.d, " 2. ;"); - - // TODO continue F and G editing tests on these data - - u.n = 0xbff8000000000000; // -1.5 - realTest("(E9.2,';')", u.d, "-0.15E+01;"); - realTest("(RN,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RD,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RU,E8.1,';')", u.d, "-0.1E+01;"); - realTest("(RZ,E8.1,';')", u.d, "-0.1E+01;"); - realTest("(RC,E8.1,';')", u.d, "-0.2E+01;"); - - u.n = 0x4004000000000000; // 2.5 - realTest("(E9.2,';')", u.d, " 0.25E+01;"); - realTest("(RN,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RD,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RU,E8.1,';')", u.d, " 0.3E+01;"); - realTest("(RZ,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RC,E8.1,';')", u.d, " 0.3E+01;"); - - u.n = 0xc004000000000000; // -2.5 - realTest("(E9.2,';')", u.d, "-0.25E+01;"); - realTest("(RN,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RD,E8.1,';')", u.d, "-0.3E+01;"); - realTest("(RU,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RZ,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RC,E8.1,';')", u.d, "-0.3E+01;"); - - u.n = 1; // least positive nonzero subnormal - realTest("(E32.17,';')", u.d, " 0.49406564584124654-323;"); - realTest("(ES32.17,';')", u.d, " 4.94065645841246544-324;"); - realTest("(EN32.17,';')", u.d, " 4.94065645841246544-324;"); - realTest("(E759.752,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "75386825064197182655334472656250-323;"); - realTest("(G0,';')", u.d, ".5-323;"); - realTest("(E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726562-323;"); - realTest("(RN,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726562-323;"); - realTest("(RD,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726562-323;"); - realTest("(RU,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726563-323;"); - realTest("(RC,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726563-323;"); - - u.n = 0x10000000000000; // least positive nonzero normal - realTest("(E723.716,';')", u.d, - " 0." - "222507385850720138309023271733240406421921598046233183055332741688720443" - "481391819585428315901251102056406733973103581100515243416155346010885601" - "238537771882113077799353200233047961014744258363607192156504694250373420" - "837525080665061665815894872049117996859163964850063590877011830487479978" - "088775374994945158045160505091539985658247081864511353793580499211598108" - "576605199243335211435239014879569960959128889160299264151106346631339366" - "347758651302937176204732563178148566435087212282863764204484681140761391" - "147706280168985324411002416144742161856716615054015428508471675290190316" - "132277889672970737312333408698898317506783884692609277397797285865965494" - "10913690954061364675687023986783152906809846172109246253967285156250-" - "307;"); - realTest("(G0,';')", u.d, ".22250738585072014-307;"); - - u.n = 0x7fefffffffffffffuLL; // greatest finite - realTest("(E32.17,';')", u.d, " 0.17976931348623157+309;"); - realTest("(E317.310,';')", u.d, - " 0." - "179769313486231570814527423731704356798070567525844996598917476803157260" - "780028538760589558632766878171540458953514382464234321326889464182768467" - "546703537516986049910576551282076245490090389328944075868508455133942304" - "583236903222948165808559332123348274797826204144723168738177180919299881" - "2504040261841248583680+309;"); - realTest("(ES317.310,';')", u.d, - " 1." - "797693134862315708145274237317043567980705675258449965989174768031572607" - "800285387605895586327668781715404589535143824642343213268894641827684675" - "467035375169860499105765512820762454900903893289440758685084551339423045" - "832369032229481658085593321233482747978262041447231687381771809192998812" - "5040402618412485836800+308;"); - realTest("(EN319.310,';')", u.d, - " 179." - "769313486231570814527423731704356798070567525844996598917476803157260780" - "028538760589558632766878171540458953514382464234321326889464182768467546" - "703537516986049910576551282076245490090389328944075868508455133942304583" - "236903222948165808559332123348274797826204144723168738177180919299881250" - "4040261841248583680000+306;"); - realTest("(G0,';')", u.d, ".17976931348623157+309;"); - - realTest("(F5.3,';')", 25., "*****;"); - realTest("(F5.3,';')", 2.5, "2.500;"); - realTest("(F5.3,';')", 0.25, "0.250;"); - realTest("(F5.3,';')", 0.025, "0.025;"); - realTest("(F5.3,';')", 0.0025, "0.003;"); - realTest("(F5.3,';')", 0.00025, "0.000;"); - realTest("(F5.3,';')", 0.000025, "0.000;"); - realTest("(F5.3,';')", -25., "*****;"); - realTest("(F5.3,';')", -2.5, "*****;"); - realTest("(F5.3,';')", -0.25, "-.250;"); - realTest("(F5.3,';')", -0.025, "-.025;"); - realTest("(F5.3,';')", -0.0025, "-.003;"); - realTest("(F5.3,';')", -0.00025, "-.000;"); - realTest("(F5.3,';')", -0.000025, "-.000;"); - - realInTest("(F18.0)", " 0", 0x0); - realInTest("(F18.0)", " ", 0x0); - realInTest("(F18.0)", " -0", 0x8000000000000000); - realInTest("(F18.0)", " 01", 0x3ff0000000000000); - realInTest("(F18.0)", " 1", 0x3ff0000000000000); - realInTest("(F18.0)", " 125.", 0x405f400000000000); - realInTest("(F18.0)", " 12.5", 0x4029000000000000); - realInTest("(F18.0)", " 1.25", 0x3ff4000000000000); - realInTest("(F18.0)", " 01.25", 0x3ff4000000000000); - realInTest("(F18.0)", " .125", 0x3fc0000000000000); - realInTest("(F18.0)", " 0.125", 0x3fc0000000000000); - realInTest("(F18.0)", " .0625", 0x3fb0000000000000); - realInTest("(F18.0)", " 0.0625", 0x3fb0000000000000); - realInTest("(F18.0)", " 125", 0x405f400000000000); - realInTest("(F18.1)", " 125", 0x4029000000000000); - realInTest("(F18.2)", " 125", 0x3ff4000000000000); - realInTest("(F18.3)", " 125", 0x3fc0000000000000); - realInTest("(-1P,F18.0)", " 125", 0x4093880000000000); // 1250 - realInTest("(1P,F18.0)", " 125", 0x4029000000000000); // 12.5 - realInTest("(BZ,F18.0)", " 125 ", 0x4093880000000000); // 1250 - realInTest("(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000); // 1.25e13 - realInTest("(DC,F18.0)", " 12,5", 0x4029000000000000); - - listInputTest(); - descrOutputTest(); - - return EndTests(); -} diff --git a/flang/unittests/RuntimeGTest/CMakeLists.txt b/flang/unittests/RuntimeGTest/CMakeLists.txt --- a/flang/unittests/RuntimeGTest/CMakeLists.txt +++ b/flang/unittests/RuntimeGTest/CMakeLists.txt @@ -1,5 +1,6 @@ add_flang_unittest(FlangRuntimeTests CharacterTest.cpp + NumericalFormatTest.cpp ) target_link_libraries(FlangRuntimeTests diff --git a/flang/unittests/RuntimeGTest/NumericalFormatTest.cpp b/flang/unittests/RuntimeGTest/NumericalFormatTest.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/RuntimeGTest/NumericalFormatTest.cpp @@ -0,0 +1,694 @@ +//===-- flang/unittests/RuntimeGTest/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 "../../runtime/descriptor.h" +#include "../../runtime/io-api.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) { + char buffer[800]; + auto *cookie{IONAME(BeginInternalFormattedOutput)( + buffer, sizeof buffer, format, std::strlen(format))}; + IONAME(OutputReal64)(cookie, x); + auto status{IONAME(EndIoStatement)(cookie)}; + + // This return is ambiguous - did the format fail to be performed, or do the + // strings not match? + if (status) + return false; + return CompareFormattedStrings(expect, std::string{buffer, sizeof buffer}); +} + +// Convert raw uint64 into double, perform format, and compare with expected +static bool CompareFormatReal( + const char *format, std::uint64_t xInt, const char *expect) { + 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); +} + +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(OutputInteger64)(cookie, 0xfeedface); + IONAME(OutputLogical)(cookie, true); + + // Ensure IO succeeded + auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_FALSE(status) << "hello: '" << format << "' failed, status " + << static_cast(status); + + // Ensure final buffer matches expected string output + static 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}; + static char buffer[numLines][lineLength]; + + // Create descriptor for entire buffer + static constexpr int staticDescriptorMaxRank{1}; + static StaticDescriptor wholeStaticDescriptor; + static Descriptor &whole{wholeStaticDescriptor.descriptor()}; + static SubscriptValue extent[]{numLines}; + whole.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength, &buffer, + staticDescriptorMaxRank, extent, CFI_attribute_pointer); + whole.Dump(); + whole.Check(); + + // Create descriptor for buffer section + static StaticDescriptor sectionStaticDescriptor; + static 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_FALSE(error) << "multiline: CFI_section failed: " << error; + section.Dump(); + section.Check(); + + // Create format string and initialize IO operation + const char *format{ + "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"}; + static auto *cookie{IONAME(BeginInternalArrayFormattedOutput)( + section, format, std::strlen(format))}; + + // 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_FALSE(status) << "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 + ASSERT_TRUE( + CompareFormattedStrings(expect, std::string{buffer[0], sizeof buffer})) + << "Expected " << expect << " but got " << buffer; +} + +TEST(IOApiTests, ListInputTest) { + static const char input[]{",1*,(5.,6..)"}; + static auto *cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)}; + + // Create real values for IO tests + static constexpr int numRealValues{6}; + static 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 + static auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_FALSE(status) << "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{33}; + static 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_FALSE(status) << "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.) "}; + 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{9}; + static char buffer[bufferSize]; + static const char *format{"(2A4)"}; + static auto *cookie{IONAME(BeginInternalFormattedOutput)( + buffer, bufferSize, format, std::strlen(format))}; + + // Create descriptor for output + static constexpr int staticDescriptorMaxRank{1}; + static StaticDescriptor staticDescriptor; + static Descriptor &desc{staticDescriptor.descriptor()}; + static constexpr int subscriptExtent{2}; + static const SubscriptValue extent[]{subscriptExtent}; + + // Manually write to descriptor buffer + static constexpr int dataLength{4}; + static 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(); + desc.Check(); + IONAME(OutputDescriptor)(cookie, desc); + + // Ensure no errors were encountered in initializing the cookie and descriptor + static auto formatStatus{IONAME(EndIoStatement)(cookie)}; + ASSERT_FALSE(formatStatus) + << "descrOutputTest: '" << format << "' failed, status " + << static_cast(formatStatus); + + // Ensure buffer matches expected output + ASSERT_TRUE( + CompareFormattedStrings("ABCDEFGH ", 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 + static auto listDirectedStatus{IONAME(EndIoStatement)(cookie)}; + ASSERT_FALSE(listDirectedStatus) + << "descrOutputTest: list-directed failed, status " + << static_cast(listDirectedStatus); + + // Ensure buffer matches expected output + ASSERT_TRUE( + CompareFormattedStrings(" ABCDEFGH", 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;"}, + {"(G0,';')", "0.;"}, + }; + + for (auto const &[format, expect] : zeroes) { + ASSERT_TRUE(CompareFormatReal(format, 0.0, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +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 ;"}, + {"(G0,';')", "1.;"}, + }; + + for (auto const &[format, expect] : ones) { + ASSERT_TRUE(CompareFormatReal(format, 1.0, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +TEST(IOApiTests, FormatNegativeOnes) { + static constexpr std::tuple negOnes[]{ + {"(E32.17,';')", " -0.10000000000000000E+01;"}, + {"(F32.17,';')", " -1.00000000000000000;"}, + {"(G32.17,';')", " -1.0000000000000000 ;"}, + {"(G0,';')", "-1.;"}, + }; + for (auto const &[format, expect] : negOnes) { + ASSERT_TRUE(CompareFormatReal(format, -1.0, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +// 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.;"}, + {"(G8.0,';')", "-0.0E+00;"}, + {"(G8.1,';')", " -0. ;"}, + {"(G0,';')", "-0.;"}, + {"(E9.1,';')", " -0.0E+00;"}, + }}, + {// +Inf + 0x7ff0000000000000, + { + {"(E9.1,';')", " Inf;"}, + {"(F9.1,';')", " Inf;"}, + {"(G9.1,';')", " Inf;"}, + {"(SP,E9.1,';')", " +Inf;"}, + {"(SP,F9.1,';')", " +Inf;"}, + {"(SP,G9.1,';')", " +Inf;"}, + {"(G0,';')", "Inf;"}, + }}, + {// -Inf + 0xfff0000000000000, + { + {"(E9.1,';')", " -Inf;"}, + {"(F9.1,';')", " -Inf;"}, + {"(G9.1,';')", " -Inf;"}, + {"(G0,';')", "-Inf;"}, + }}, + {// NaN + 0x7ff0000000000001, + { + {"(E9.1,';')", " NaN;"}, + {"(F9.1,';')", " NaN;"}, + {"(G9.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;"}, + {"(G0,';')", "NaN;"}, + }}, + {// 0.1 rounded + 0x3fb999999999999a, + { + {"(E62.55,';')", + " 0.1000000000000000055511151231257827021181583404541015625E+" + "00;"}, + {"(E0.0,';')", "0.E+00;"}, + {"(E0.55,';')", + "0.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;"}, + }}, + {// 1.5 + 0x3ff8000000000000, + { + {"(E9.2,';')", " 0.15E+01;"}, + {"(F4.1,';')", " 1.5;"}, + {"(G7.1,';')", " 2. ;"}, + {"(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;"}, + }}, + {// 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;"}, + }}, + {// -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;"}, + }}, + {// 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,';')", ".5-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;"}, + }}, + {// least positive nonzero normal + 0x10000000000000, + { + {"(E723.716,';')", + " 0." + "222507385850720138309023271733240406421921598046233183055332" + "741688720443481391819585428315901251102056406733973103581100" + "515243416155346010885601238537771882113077799353200233047961" + "014744258363607192156504694250373420837525080665061665815894" + "872049117996859163964850063590877011830487479978088775374994" + "945158045160505091539985658247081864511353793580499211598108" + "576605199243335211435239014879569960959128889160299264151106" + "346631339366347758651302937176204732563178148566435087212282" + "863764204484681140761391147706280168985324411002416144742161" + "856716615054015428508471675290190316132277889672970737312333" + "408698898317506783884692609277397797285865965494109136909540" + "61364675687023986783152906809846172109246253967285156250-" + "307;"}, + {"(G0,';')", ".22250738585072014-307;"}, + }}, + {// 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,';')", ".17976931348623157+309;"}, + }}, + }; + + for (auto const &[value, cases] : testCases) { + for (auto const &[format, expect] : cases) { + ASSERT_TRUE(CompareFormatReal(format, value, expect)) + << "Failed to format " << format << ", expected " << expect; + } + } + + using IndividualTestCaseTy = std::tuple; + static 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;"}, + }; + + for (auto const &[format, value, expect] : individualTestCases) { + ASSERT_TRUE(CompareFormatReal(format, value, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +//------------------------------------------------------------------------------ +/// Tests for input formatting real values +//------------------------------------------------------------------------------ + +// Ensure double input values correctly map to raw uint64 values +TEST(IOApiTests, FormatDoubleInputValues) { + using TestCaseTy = std::tuple; + static std::vector testCases{ + {"(F18.0)", " 0", 0x0}, + {"(F18.0)", " ", 0x0}, + {"(F18.0)", " -0", 0x8000000000000000}, + {"(F18.0)", " 01", 0x3ff0000000000000}, + {"(F18.0)", " 1", 0x3ff0000000000000}, + {"(F18.0)", " 125.", 0x405f400000000000}, + {"(F18.0)", " 12.5", 0x4029000000000000}, + {"(F18.0)", " 1.25", 0x3ff4000000000000}, + {"(F18.0)", " 01.25", 0x3ff4000000000000}, + {"(F18.0)", " .125", 0x3fc0000000000000}, + {"(F18.0)", " 0.125", 0x3fc0000000000000}, + {"(F18.0)", " .0625", 0x3fb0000000000000}, + {"(F18.0)", " 0.0625", 0x3fb0000000000000}, + {"(F18.0)", " 125", 0x405f400000000000}, + {"(F18.1)", " 125", 0x4029000000000000}, + {"(F18.2)", " 125", 0x3ff4000000000000}, + {"(F18.3)", " 125", 0x3fc0000000000000}, + {"(-1P,F18.0)", " 125", 0x4093880000000000}, // 1250 + {"(1P,F18.0)", " 125", 0x4029000000000000}, // 12.5 + {"(BZ,F18.0)", " 125 ", 0x4093880000000000}, // 1250 + {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13 + {"(DC,F18.0)", " 12,5", 0x4029000000000000}, + }; + for (auto const &[format, data, want] : testCases) { + static 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}; + static char iomsg[bufferSize]; + std::memset(iomsg, '\0', bufferSize - 1); + + // Ensure no errors were encountered reading input buffer into union value + IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1); + static auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_FALSE(status) << '\'' << format << "' failed reading '" << data + << "', status " << static_cast(status) + << " iomsg '" << iomsg << "'"; + + // Ensure raw uint64 value matches expected conversion from double + ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data + << "', want 0x" << std::hex << want << ", got 0x" + << u.raw; + } +}