[flang] Enforce digit limits
authorpeter klausler <pklausler@nvidia.com>
Wed, 14 Aug 2019 20:10:10 +0000 (13:10 -0700)
committerpeter klausler <pklausler@nvidia.com>
Fri, 23 Aug 2019 18:31:16 +0000 (11:31 -0700)
Original-commit: flang-compiler/f18@33600e127dd8ed747d10063d55d8b1eb99bb999b
Reviewed-on: https://github.com/flang-compiler/f18/pull/671
Tree-same-pre-rewrite: false

flang/lib/decimal/binary-to-decimal.cc
flang/lib/decimal/decimal-to-binary.cc
flang/lib/decimal/decimal.h

index d0bb7a0..a776534 100644 (file)
@@ -111,9 +111,9 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::BigRadixFloatingPointNumber(
 template<int PREC, int LOG10RADIX>
 ConversionToDecimalResult
 BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToDecimal(
-    char *buffer, std::size_t n, int flags, int digits) const {
-  if (n < (digits_ + 1) * LOG10RADIX) {  // pmk revisit
-    return {nullptr, 0, 0};
+    char *buffer, std::size_t n, int flags, int maxDigits) const {
+  if (n < 3 + digits_ * LOG10RADIX || maxDigits < 1) {
+    return {nullptr, 0, 0, Overflow};
   }
   char *start{buffer};
   if (isNegative_) {
@@ -121,12 +121,12 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToDecimal(
   } else if (flags & AlwaysSign) {
     *start++ = '+';
   }
-  char *p{start};
   if (IsZero()) {
-    *p++ = '0';
-    *p = '\0';
-    return {buffer, static_cast<std::size_t>(p - buffer), 0};
+    *start++ = '0';
+    *start = '\0';
+    return {buffer, static_cast<std::size_t>(start - buffer), 0};
   }
+  char *p{start};
   static_assert((LOG10RADIX % 2) == 0, "radix not a power of 100");
   static const char lut[] = "0001020304050607080910111213141516171819"
                             "2021222324252627282930313233343536373839"
@@ -157,12 +157,46 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToDecimal(
       *p++ = q[1];
     }
   }
+  // Adjust exponent so the effective decimal point is to
+  // the left of the first digit.
   int expo = exponent_ + p - start;
+  // Trim trailing zeroes.
   while (p[-1] == '0') {
     --p;
   }
-  *p = '\0';
-  return {buffer, static_cast<std::size_t>(p - buffer), expo};
+  if (p <= start + maxDigits) {
+    *p = '\0';
+    return {buffer, static_cast<std::size_t>(p - buffer), expo, Exact};
+  } else {
+    // Apply a digit limit, possibly with rounding.
+    char *end{start + maxDigits};
+    bool incr{false};
+    switch (rounding_) {
+    case RoundNearest:
+    case RoundDefault:
+      incr =
+          *end > '5' || (*end == '5' && (p > end || ((p[-1] - '0') & 1) != 0));
+      break;
+    case RoundUp: incr = !isNegative_; break;
+    case RoundDown: incr = isNegative_; break;
+    case RoundToZero: break;
+    case RoundCompatible: incr = *end >= '5'; break;
+    }
+    p = end;
+    if (incr) {
+      while (p > start && p[-1] == '9') {
+        --p;
+      }
+      if (p == start) {
+        *p++ = '1';
+        ++expo;
+      } else {
+        ++p[-1];
+      }
+    }
+    *p = '\0';
+    return {buffer, static_cast<std::size_t>(p - buffer), expo, Inexact};
+  }
 }
 
 template<int PREC, int LOG10RADIX>
@@ -261,9 +295,9 @@ ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, int flags,
     int digits, enum FortranRounding rounding,
     BinaryFloatingPointNumber<PREC> x) {
   if (x.IsNaN()) {
-    return {"NaN", 3, 0};
+    return {"NaN", 3, 0, Invalid};
   } else if (x.IsInfinite()) {
-    return {x.IsNegative() ? "-Inf" : "+Inf", 4, 0};
+    return {x.IsNegative() ? "-Inf" : "+Inf", 4, 0, Exact};
   } else {
     using Binary = BinaryFloatingPointNumber<PREC>;
     using Big = BigRadixFloatingPointNumber<PREC>;
@@ -285,7 +319,7 @@ ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, int flags,
       }
       number.Minimize(Big{less, rounding}, Big{more, rounding});
     }
-    number.Clamp(digits);
+    number.Clamp(digits);  // todo pmk retain Clamp?
     return number.ConvertToDecimal(buffer, size, flags, digits);
   }
 }
index 12fa00e..4160417 100644 (file)
@@ -194,7 +194,7 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
     flags |= Inexact;
   }
   if (fraction == 0 && guard <= topBit) {
-    return {Binary{}, static_cast<enum BinaryConversionResultFlags>(flags)};
+    return {Binary{}, static_cast<enum ConversionResultFlags>(flags)};
   }
   // The value is nonzero; normalize it.
   while (fraction < topBit && expo > 1) {
@@ -238,7 +238,7 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
     fraction &= ~topBit;
   }
   raw |= fraction;
-  return {Binary(raw), static_cast<enum BinaryConversionResultFlags>(flags)};
+  return {Binary(raw), static_cast<enum ConversionResultFlags>(flags)};
 }
 
 template<int PREC, int LOG10RADIX>
index 7da1fd9..5fcd87a 100644 (file)
 #include "binary-floating-point.h"
 #include <stddef.h>
 
+// Binary-to-decimal conversions (formatting) produce a sequence of decimal
+// digit characters in a NUL-terminated user-supplied buffer that constitute
+// a decimal fraction (or zero), accompanied by a decimal exponent that
+// you'll get to adjust and format yourself.  There can be a leading sign
+// character.
+// Negative zero is "-0".  The result can also be "NaN", "Inf", "+Inf",
+// or "-Inf".
+// If the conversion can't fit in the user-supplied buffer, a null pointer
+// is returned.
+
+enum ConversionResultFlags {
+  Exact = 0,
+  Overflow = 1,
+  Inexact = 2,
+  Invalid = 4,
+};
+
 struct ConversionToDecimalResult {
   const char *str; /* may not be original buffer pointer; null if overflow */
-  size_t length; /* not including NUL terminator */
+  size_t length; /* does not include NUL terminator */
   int decimalExponent; /* assuming decimal point to the left of first digit */
+  enum ConversionResultFlags flags;
 };
 
 enum FortranRounding {
@@ -45,13 +63,6 @@ enum DecimalConversionFlags {
   AlwaysSign = 2, /* emit leading '+' if not negative */
 };
 
-enum BinaryConversionResultFlags {
-  Exact = 0,
-  Overflow = 1,
-  Inexact = 2,
-  Invalid = 4,
-};
-
 #ifdef __cplusplus
 namespace Fortran::decimal {
 
@@ -75,7 +86,7 @@ extern template ConversionToDecimalResult ConvertToDecimal<112>(char *, size_t,
 
 template<int PREC> struct ConversionToBinaryResult {
   BinaryFloatingPointNumber<PREC> binary;
-  enum BinaryConversionResultFlags flags { Exact };
+  enum ConversionResultFlags flags { Exact };
 };
 
 template<int PREC>