[flang] Cope better with ridiculously large exponents on input
authorpeter klausler <pklausler@nvidia.com>
Thu, 22 Aug 2019 20:58:50 +0000 (13:58 -0700)
committerpeter klausler <pklausler@nvidia.com>
Fri, 23 Aug 2019 18:31:47 +0000 (11:31 -0700)
Original-commit: flang-compiler/f18@e55cc39bad40f6419aadf26101dc6e9c3ed3fda9
Reviewed-on: https://github.com/flang-compiler/f18/pull/671
Tree-same-pre-rewrite: false

flang/lib/decimal/big-radix-floating-point.h
flang/lib/decimal/binary-floating-point.h
flang/lib/decimal/decimal-to-binary.cc
flang/lib/evaluate/real.h

index 3fdad82..dde8fcd 100644 (file)
@@ -63,8 +63,7 @@ private:
 
   // The base-2 logarithm of the least significant bit that can arise
   // in a subnormal IEEE floating-point number.
-  static constexpr int minLog2AnyBit{
-      -int{Real::exponentBias} - Real::precision};
+  static constexpr int minLog2AnyBit{-Real::exponentBias - Real::precision};
 
   // The number of Digits needed to represent the smallest subnormal.
   static constexpr int maxDigits{3 - minLog2AnyBit / log10Radix};
@@ -314,6 +313,16 @@ private:
 
   bool ParseNumber(const char *&, bool &inexact);
 
+  using Raw = typename Real::RawType;
+  constexpr Raw SignBit() const { return Raw{isNegative_} << (Real::bits - 1); }
+  constexpr Raw Infinity() const {
+    return (Raw{Real::maxExponent} << Real::significandBits) | SignBit();
+  }
+  static constexpr Raw NaN() {
+    return (Raw{Real::maxExponent} << Real::significandBits) |
+        (Raw{1} << (Real::significandBits - 2));
+  }
+
   Digit digit_[maxDigits];  // in little-endian order: digit_[0] is LSD
   int digits_{0};  // # of elements in digit_[] array; zero when zero
   int digitLimit_{maxDigits};  // precision clamp
index 7fd86f6..b7ad8ef 100644 (file)
@@ -46,6 +46,9 @@ static constexpr int BitsForPrecision(int prec) {
   }
 }
 
+// LOG10(2.)*1E12
+static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664};
+
 template<int PRECISION> struct BinaryFloatingPointNumber {
   static constexpr int precision{PRECISION};
   static constexpr int bits{BitsForPrecision(precision)};
@@ -57,6 +60,8 @@ template<int PRECISION> struct BinaryFloatingPointNumber {
   static constexpr int maxExponent{(1 << exponentBits) - 1};
   static constexpr int exponentBias{maxExponent / 2};
   static constexpr RawType significandMask{(RawType{1} << significandBits) - 1};
+  static constexpr int RANGE{static_cast<int>(
+      (exponentBias - 1) * ScaledLogBaseTenOfTwo / 1000000000000)};
 
   BinaryFloatingPointNumber() {}  // zero
   BinaryFloatingPointNumber(const BinaryFloatingPointNumber &that) = default;
@@ -67,7 +72,7 @@ template<int PRECISION> struct BinaryFloatingPointNumber {
       BinaryFloatingPointNumber &&that) = default;
 
   template<typename A> explicit constexpr BinaryFloatingPointNumber(A x) {
-    static_assert(sizeof raw == sizeof x);
+    static_assert(sizeof raw <= sizeof x);
     std::memcpy(reinterpret_cast<void *>(&raw),
         reinterpret_cast<const void *>(&x), sizeof raw);
   }
index 74add6b..5f25b23 100644 (file)
@@ -116,9 +116,21 @@ bool BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ParseNumber(
     }
     if (*q >= '0' && *q <= '9') {
       int expo{0};
-      for (int j{0}; j < 8 && *q >= '0' && *q <= '9'; ++j) {
+      while (*q == '0') {
+        ++q;
+      }
+      const char *expDig{q};
+      while (*q >= '0' && *q <= '9') {
         expo = 10 * expo + *q++ - '0';
       }
+      if (q >= expDig + 8) {
+        // There's a ridiculous number of nonzero exponent digits.
+        // The decimal->binary conversion routine will cope with
+        // returning 0 or Inf, but we must ensure that "expo" didn't
+        // overflow back around to something legal.
+        expo = 10 * Real::RANGE;
+        exponent_ = 0;
+      }
       p = q;  // exponent was valid
       if (negExpo) {
         exponent_ -= expo;
@@ -260,28 +272,27 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
 template<int PREC, int LOG10RADIX>
 ConversionToBinaryResult<PREC>
 BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToBinary() {
-  using Binary = BinaryFloatingPointNumber<PREC>;
   // On entry, *this holds a multi-precision integer value in a radix of a
   // large power of ten.  Its radix point is defined to be to the right of its
   // digits, and "exponent_" is the power of ten by which it is to be scaled.
   Normalize();
   if (digits_ == 0) {  // zero value
-    if (isNegative_) {
-      using Raw = typename Binary::RawType;
-      Raw negZero{Raw{1} << (Binary::bits - 1)};
-      return {Binary{negZero}};
-    } else {
-      return {Binary{}};
-    }
+    return {Real{SignBit()}};
   }
-  // The value is not zero.
-  // x = D. * 10.**E
-  IntermediateFloat<PREC> f;
+  // The value is not zero:  x = D. * 10.**E
   // Shift our perspective on the radix (& decimal) point so that
   // it sits to the *left* of the digits: i.e., x = .D * 10.**E
   exponent_ += digits_ * log10Radix;
+  // Sanity checks for ridiculous exponents
+  static constexpr int crazy{2 * Real::RANGE + log10Radix};
+  if (exponent_ < -crazy) {  // underflow to +/-0.
+    return {Real{SignBit()}, Inexact};
+  } else if (exponent_ > crazy) {  // overflow to +/-Inf.
+    return {Real{Infinity()}, Overflow};
+  }
   // Apply any negative decimal exponent by multiplication
   // by a power of two, adjusting the binary exponent to compensate.
+  IntermediateFloat<PREC> f;
   while (exponent_ < log10Radix) {
     // x = 0.D * 10.**E * 2.**(f.ex) -> 512 * 0.D * 10.**E * 2.**(f.ex-9)
     f.AdjustExponent(-9);
@@ -355,30 +366,24 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToBinary(const char *&p) {
   } else {
     // Could not parse a decimal floating-point number.  p has been
     // advanced over any leading spaces.
-    using Binary = BinaryFloatingPointNumber<PREC>;
-    using Raw = typename Binary::RawType;
-    static constexpr Raw inf{
-        Raw{Binary::maxExponent} << Binary::significandBits};
-    static constexpr Raw nan{inf | (Raw{1} << (Binary::significandBits - 2))};
-    static constexpr Raw negInf{inf | (Raw{1} << (Binary::bits - 1))};
     if (toupper(p[0]) == 'N' && toupper(p[1]) == 'A' && toupper(p[2]) == 'N') {
       // NaN
       p += 3;
-      return {Binary{nan}};
+      return {Real{NaN()}};
     } else {
       // Try to parse Inf, maybe with a sign
       const char *q{p};
-      bool isNegative{*q == '-'};
+      isNegative_ = *q == '-';
       if (*q == '-' || *q == '+') {
         ++q;
       }
       if (toupper(q[0]) == 'I' && toupper(q[1]) == 'N' &&
           toupper(q[2]) == 'F') {
         p = q + 3;
-        return {Binary(isNegative ? negInf : inf)};
+        return {Real{Infinity()}};
       } else {
         // Invalid input
-        return {Binary{nan}, Invalid};
+        return {Real{NaN()}, Invalid};
       }
     }
   }
index 86db3f9..edaee19 100644 (file)
@@ -30,6 +30,9 @@
 
 namespace Fortran::evaluate::value {
 
+// LOG10(2.)*1E12
+static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664};
+
 // Models IEEE binary floating-point numbers (IEEE 754-2008,
 // ISO/IEC/IEEE 60559.2011).  The first argument to this
 // class template must be (or look like) an instance of Integer<>;
@@ -147,11 +150,6 @@ public:
     return tiny;
   }
 
-private:
-  // LOG10(2.)*1E12
-  static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664};
-
-public:
   static constexpr int DIGITS{precision};
   static constexpr int PRECISION{static_cast<int>(
       (precision - 1) * ScaledLogBaseTenOfTwo / 1000000000000)};