diff --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp --- a/flang/runtime/edit-output.cpp +++ b/flang/runtime/edit-output.cpp @@ -154,7 +154,8 @@ int digits = end - p; int leadingZeroes{0}; int editWidth{edit.width.value_or(0)}; - if (edit.digits && digits <= *edit.digits) { // Iw.m + if (edit.descriptor == 'I' && edit.digits && digits <= *edit.digits) { + // Only Iw.m can produce leading zeroes, not Gw.d (F'202X 13.7.5.2.2) if (*edit.digits == 0 && n == 0) { // Iw.0 with zero value: output field must be blank. For I0.0 // and a zero value, emit one blank character. diff --git a/flang/unittests/Runtime/NumericalFormatTest.cpp b/flang/unittests/Runtime/NumericalFormatTest.cpp --- a/flang/unittests/Runtime/NumericalFormatTest.cpp +++ b/flang/unittests/Runtime/NumericalFormatTest.cpp @@ -57,6 +57,22 @@ 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) { @@ -693,6 +709,71 @@ } } +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 formatting real values //------------------------------------------------------------------------------