[flang-commits] [flang] d1b09ad - [flang] Fix rounding edge case in F output editing

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Fri Nov 12 11:16:32 PST 2021


Author: Peter Klausler
Date: 2021-11-12T11:16:25-08:00
New Revision: d1b09adeebe8e2faf2c18fd24b9f83e89087a1e8

URL: https://github.com/llvm/llvm-project/commit/d1b09adeebe8e2faf2c18fd24b9f83e89087a1e8
DIFF: https://github.com/llvm/llvm-project/commit/d1b09adeebe8e2faf2c18fd24b9f83e89087a1e8.diff

LOG: [flang] Fix rounding edge case in F output editing

When an Fw.d output edit descriptor has a "d" value exactly
equal to the number of zeroes after the decimal point for a value
(e.g., 0.07 with F5.1), the Fw.d output editing code needs to
do the rounding itself to either 0.0 or 0.1 after performing
a conversion without rounding (to avoid 0.04999 rounding up twice).

Differential Revision: https://reviews.llvm.org/D113698

Added: 
    

Modified: 
    flang/runtime/edit-output.cpp
    flang/runtime/edit-output.h
    flang/unittests/Runtime/NumericalFormatTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp
index 09dd40674c38..079d8aefefad 100644
--- a/flang/runtime/edit-output.cpp
+++ b/flang/runtime/edit-output.cpp
@@ -159,13 +159,10 @@ bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) {
 
 template <int binaryPrecision>
 decimal::ConversionToDecimalResult RealOutputEditing<binaryPrecision>::Convert(
-    int significantDigits, const DataEdit &edit, int flags) {
-  if (edit.modes.editingFlags & signPlus) {
-    flags |= decimal::AlwaysSign;
-  }
+    int significantDigits, enum decimal::FortranRounding rounding, int flags) {
   auto converted{decimal::ConvertToDecimal<binaryPrecision>(buffer_,
       sizeof buffer_, static_cast<enum decimal::DecimalConversionFlags>(flags),
-      significantDigits, edit.modes.round, x_)};
+      significantDigits, rounding, x_)};
   if (!converted.str) { // overflow
     io_.GetIoErrorHandler().Crash(
         "RealOutputEditing::Convert : buffer size %zd was insufficient",
@@ -181,6 +178,9 @@ bool RealOutputEditing<binaryPrecision>::EditEorDOutput(const DataEdit &edit) {
   int editWidth{edit.width.value_or(0)}; // 'w' field
   int significantDigits{editDigits};
   int flags{0};
+  if (edit.modes.editingFlags & signPlus) {
+    flags |= decimal::AlwaysSign;
+  }
   if (editWidth == 0) { // "the processor selects the field width"
     if (edit.digits.has_value()) { // E0.d
       editWidth = editDigits + 6; // -.666E+ee
@@ -204,7 +204,7 @@ bool RealOutputEditing<binaryPrecision>::EditEorDOutput(const DataEdit &edit) {
   // In EN editing, multiple attempts may be necessary, so it's in a loop.
   while (true) {
     decimal::ConversionToDecimalResult converted{
-        Convert(significantDigits, edit, flags)};
+        Convert(significantDigits, edit.modes.round, flags)};
     if (IsInfOrNaN(converted)) {
       return EmitPrefix(edit, converted.length, editWidth) &&
           io_.Emit(converted.str, converted.length) && EmitSuffix(edit);
@@ -261,7 +261,11 @@ template <int binaryPrecision>
 bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) {
   int fracDigits{edit.digits.value_or(0)}; // 'd' field
   const int editWidth{edit.width.value_or(0)}; // 'w' field
+  enum decimal::FortranRounding rounding{edit.modes.round};
   int flags{0};
+  if (edit.modes.editingFlags & signPlus) {
+    flags |= decimal::AlwaysSign;
+  }
   if (editWidth == 0) { // "the processor selects the field width"
     if (!edit.digits.has_value()) { // F0
       flags |= decimal::Minimize;
@@ -274,13 +278,16 @@ bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) {
   bool canIncrease{true};
   while (true) {
     decimal::ConversionToDecimalResult converted{
-        Convert(extraDigits + fracDigits, edit, flags)};
+        Convert(extraDigits + fracDigits, rounding, flags)};
     if (IsInfOrNaN(converted)) {
       return EmitPrefix(edit, converted.length, editWidth) &&
           io_.Emit(converted.str, converted.length) && EmitSuffix(edit);
     }
     int scale{IsZero() ? 1 : edit.modes.scale}; // kP
     int expo{converted.decimalExponent + scale};
+    int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0};
+    int convertedDigits{static_cast<int>(converted.length) - signLength};
+    int trailingOnes{0};
     if (expo > extraDigits && extraDigits >= 0 && canIncrease) {
       extraDigits = expo;
       if (!edit.digits.has_value()) { // F0
@@ -288,26 +295,42 @@ bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) {
       }
       canIncrease = false; // only once
       continue;
+    } else if (expo == -fracDigits && convertedDigits > 0) {
+      if (rounding != decimal::FortranRounding::RoundToZero) {
+        // Convert again without rounding so that we can round here
+        rounding = decimal::FortranRounding::RoundToZero;
+        continue;
+      } else if (converted.str[signLength] >= '5') {
+        // Value rounds up to a scaled 1 (e.g., 0.06 for F5.1 -> 0.1)
+        ++expo;
+        convertedDigits = 0;
+        trailingOnes = 1;
+      } else {
+        // Value rounds down to zero
+        expo = 0;
+        convertedDigits = 0;
+      }
     } else if (expo < extraDigits && extraDigits > -fracDigits) {
       extraDigits = std::max(expo, -fracDigits);
       continue;
     }
-    int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0};
-    int convertedDigits{static_cast<int>(converted.length) - signLength};
     int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))};
     int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)};
     int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))};
     int digitsAfterPoint{convertedDigits - digitsBeforePoint};
     int trailingZeroes{flags & decimal::Minimize
             ? 0
-            : std::max(0, fracDigits - (zeroesAfterPoint + digitsAfterPoint))};
+            : std::max(0,
+                  fracDigits -
+                      (zeroesAfterPoint + digitsAfterPoint + trailingOnes))};
     if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint +
-            digitsAfterPoint + trailingZeroes ==
+            digitsAfterPoint + trailingOnes + trailingZeroes ==
         0) {
       zeroesBeforePoint = 1; // "." -> "0."
     }
     int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint +
-        1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes};
+        1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingOnes +
+        trailingZeroes};
     int width{editWidth > 0 ? editWidth : totalLength};
     if (totalLength > width) {
       return io_.EmitRepeated('*', width);
@@ -323,6 +346,7 @@ bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) {
         io_.EmitRepeated('0', zeroesAfterPoint) &&
         io_.Emit(
             converted.str + signLength + digitsBeforePoint, digitsAfterPoint) &&
+        io_.EmitRepeated('1', trailingOnes) &&
         io_.EmitRepeated('0', trailingZeroes) &&
         io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit);
   }
@@ -337,8 +361,12 @@ DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) {
   if (!edit.width.has_value() || (*edit.width > 0 && significantDigits == 0)) {
     return edit; // Gw.0 -> Ew.0 for w > 0
   }
+  int flags{0};
+  if (edit.modes.editingFlags & signPlus) {
+    flags |= decimal::AlwaysSign;
+  }
   decimal::ConversionToDecimalResult converted{
-      Convert(significantDigits, edit)};
+      Convert(significantDigits, edit.modes.round, flags)};
   if (IsInfOrNaN(converted)) {
     return edit;
   }
@@ -365,7 +393,7 @@ DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) {
 template <int binaryPrecision>
 bool RealOutputEditing<binaryPrecision>::EditListDirectedOutput(
     const DataEdit &edit) {
-  decimal::ConversionToDecimalResult converted{Convert(1, edit)};
+  decimal::ConversionToDecimalResult converted{Convert(1, edit.modes.round)};
   if (IsInfOrNaN(converted)) {
     return EditEorDOutput(edit);
   }

diff  --git a/flang/runtime/edit-output.h b/flang/runtime/edit-output.h
index 843310e992bc..bcb6fb0b6bfa 100644
--- a/flang/runtime/edit-output.h
+++ b/flang/runtime/edit-output.h
@@ -84,7 +84,7 @@ template <int KIND> class RealOutputEditing : public RealOutputEditingBase {
   bool IsZero() const { return x_.IsZero(); }
 
   decimal::ConversionToDecimalResult Convert(
-      int significantDigits, const DataEdit &, int flags = 0);
+      int significantDigits, enum decimal::FortranRounding, int flags = 0);
 
   BinaryFloatingPoint x_;
   char buffer_[BinaryFloatingPoint::maxDecimalConversionDigits +

diff  --git a/flang/unittests/Runtime/NumericalFormatTest.cpp b/flang/unittests/Runtime/NumericalFormatTest.cpp
index 0dd274713696..ee60956e8efd 100644
--- a/flang/unittests/Runtime/NumericalFormatTest.cpp
+++ b/flang/unittests/Runtime/NumericalFormatTest.cpp
@@ -633,6 +633,8 @@ TEST(IOApiTests, FormatDoubleValues) {
       {"(F5.3,';')", 0.099999, "0.100;"},
       {"(F5.3,';')", 0.0099999, "0.010;"},
       {"(F5.3,';')", 0.00099999, "0.001;"},
+      {"(F5.3,';')", 0.0005, "0.001;"},
+      {"(F5.3,';')", 0.00049999, "0.000;"},
       {"(F5.3,';')", 0.000099999, "0.000;"},
       {"(F5.3,';')", -99.999, "*****;"},
       {"(F5.3,';')", -9.9999, "*****;"},
@@ -640,6 +642,8 @@ TEST(IOApiTests, FormatDoubleValues) {
       {"(F5.3,';')", -0.099999, "-.100;"},
       {"(F5.3,';')", -0.0099999, "-.010;"},
       {"(F5.3,';')", -0.00099999, "-.001;"},
+      {"(F5.3,';')", -0.0005, "-.001;"},
+      {"(F5.3,';')", -0.00049999, "-.000;"},
       {"(F5.3,';')", -0.000099999, "-.000;"},
   };
 


        


More information about the flang-commits mailing list