[flang-commits] [flang] 6fec2c4 - [flang] Fix x87 binary->decimal

peter klausler via flang-commits flang-commits at lists.llvm.org
Mon May 4 12:05:17 PDT 2020


Author: peter klausler
Date: 2020-05-04T12:04:08-07:00
New Revision: 6fec2c4402ce34edc5c27202422007b12bb25797

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

LOG: [flang] Fix x87 binary->decimal

Summary:
Fix decimal formatting of 80-bit x87 values; the calculation ofnearest neighbor values failed to account for the explicitmost significant bit in that format.

Replace MultiplyByRounded with MultiplyBy in binary->decimal conversions,
since rounding won't happen and the name was misleading; then remove
dead code, and migrate LoseLeastSignificantDigit() from one source file
to another where it's still needed.

Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby

Reviewed By: tskeith

Subscribers: llvm-commits, flang-commits

Tags: #flang, #llvm

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

Added: 
    

Modified: 
    flang/include/flang/Common/uint128.h
    flang/include/flang/Decimal/binary-floating-point.h
    flang/lib/Decimal/big-radix-floating-point.h
    flang/lib/Decimal/binary-to-decimal.cpp
    flang/lib/Decimal/decimal-to-binary.cpp
    flang/runtime/edit-output.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index 996b13778322..eecf4a8ba114 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -12,8 +12,11 @@
 #ifndef FORTRAN_COMMON_UINT128_H_
 #define FORTRAN_COMMON_UINT128_H_
 
+// Define AVOID_NATIVE_UINT128_T to force the use of UnsignedInt128 below
+// instead of the C++ compiler's native 128-bit unsigned integer type, if
+// it has one.
 #ifndef AVOID_NATIVE_UINT128_T
-#define AVOID_NATIVE_UINT128_T 1 // always use this code for now for testing
+#define AVOID_NATIVE_UINT128_T 0
 #endif
 
 #include "leading-zero-bit-count.h"

diff  --git a/flang/include/flang/Decimal/binary-floating-point.h b/flang/include/flang/Decimal/binary-floating-point.h
index 7cc8384861fb..24c23b0ce5ce 100644
--- a/flang/include/flang/Decimal/binary-floating-point.h
+++ b/flang/include/flang/Decimal/binary-floating-point.h
@@ -22,9 +22,8 @@
 namespace Fortran::decimal {
 
 template <int BINARY_PRECISION>
-struct BinaryFloatingPointNumber
-    : public common::RealDetails<BINARY_PRECISION> {
-
+class BinaryFloatingPointNumber : public common::RealDetails<BINARY_PRECISION> {
+public:
   using Details = common::RealDetails<BINARY_PRECISION>;
   using Details::bits;
   using Details::decimalPrecision;
@@ -50,21 +49,23 @@ struct BinaryFloatingPointNumber
   constexpr BinaryFloatingPointNumber &operator=(
       BinaryFloatingPointNumber &&that) = default;
 
+  RawType raw() const { return raw_; }
+
   template <typename A> explicit constexpr BinaryFloatingPointNumber(A x) {
-    static_assert(sizeof raw <= sizeof x);
-    std::memcpy(reinterpret_cast<void *>(&raw),
-        reinterpret_cast<const void *>(&x), sizeof raw);
+    static_assert(sizeof raw_ <= sizeof x);
+    std::memcpy(reinterpret_cast<void *>(&raw_),
+        reinterpret_cast<const void *>(&x), sizeof raw_);
   }
 
   constexpr int BiasedExponent() const {
     return static_cast<int>(
-        (raw >> significandBits) & ((1 << exponentBits) - 1));
+        (raw_ >> significandBits) & ((1 << exponentBits) - 1));
   }
   constexpr int UnbiasedExponent() const {
     int biased{BiasedExponent()};
     return biased - exponentBias + (biased == 0);
   }
-  constexpr RawType Significand() const { return raw & significandMask; }
+  constexpr RawType Significand() const { return raw_ & significandMask; }
   constexpr RawType Fraction() const {
     RawType sig{Significand()};
     if (isImplicitMSB && BiasedExponent() > 0) {
@@ -74,7 +75,7 @@ struct BinaryFloatingPointNumber
   }
 
   constexpr bool IsZero() const {
-    return (raw & ((RawType{1} << (bits - 1)) - 1)) == 0;
+    return (raw_ & ((RawType{1} << (bits - 1)) - 1)) == 0;
   }
   constexpr bool IsNaN() const {
     return BiasedExponent() == maxExponent && Significand() != 0;
@@ -86,11 +87,39 @@ struct BinaryFloatingPointNumber
     return BiasedExponent() == maxExponent - 1 &&
         Significand() == significandMask;
   }
-  constexpr bool IsNegative() const { return ((raw >> (bits - 1)) & 1) != 0; }
+  constexpr bool IsNegative() const { return ((raw_ >> (bits - 1)) & 1) != 0; }
+
+  constexpr void Negate() { raw_ ^= RawType{1} << (bits - 1); }
+
+  // For calculating the nearest neighbors of a floating-point value
+  constexpr void Previous() {
+    RemoveExplicitMSB();
+    --raw_;
+    InsertExplicitMSB();
+  }
+  constexpr void Next() {
+    RemoveExplicitMSB();
+    ++raw_;
+    InsertExplicitMSB();
+  }
 
-  constexpr void Negate() { raw ^= RawType{1} << (bits - 1); }
+private:
+  constexpr void RemoveExplicitMSB() {
+    if constexpr (!isImplicitMSB) {
+      raw_ = (raw_ & (significandMask >> 1)) | ((raw_ & ~significandMask) >> 1);
+    }
+  }
+  constexpr void InsertExplicitMSB() {
+    if constexpr (!isImplicitMSB) {
+      constexpr RawType mask{significandMask >> 1};
+      raw_ = (raw_ & mask) | ((raw_ & ~mask) << 1);
+      if (BiasedExponent() > 0) {
+        raw_ |= RawType{1} << (significandBits - 1);
+      }
+    }
+  }
 
-  RawType raw{0};
+  RawType raw_{0};
 };
 } // namespace Fortran::decimal
 #endif

diff  --git a/flang/lib/Decimal/big-radix-floating-point.h b/flang/lib/Decimal/big-radix-floating-point.h
index 9c940fe7d6d8..cc203e90bc91 100644
--- a/flang/lib/Decimal/big-radix-floating-point.h
+++ b/flang/lib/Decimal/big-radix-floating-point.h
@@ -27,6 +27,7 @@
 #include "flang/Common/unsigned-const-division.h"
 #include "flang/Decimal/binary-floating-point.h"
 #include "flang/Decimal/decimal.h"
+#include "llvm/Support/raw_ostream.h"
 #include <cinttypes>
 #include <limits>
 #include <type_traits>
@@ -111,6 +112,8 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
   void Minimize(
       BigRadixFloatingPointNumber &&less, BigRadixFloatingPointNumber &&more);
 
+  llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
+
 private:
   BigRadixFloatingPointNumber(const BigRadixFloatingPointNumber &that)
       : digits_{that.digits_}, exponent_{that.exponent_},
@@ -283,14 +286,6 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
     }
   }
 
-  template <int N> void MultiplyByRounded() {
-    if (int carry{MultiplyBy<N>()}) {
-      LoseLeastSignificantDigit();
-      digit_[digits_ - 1] += carry;
-      exponent_ += log10Radix;
-    }
-  }
-
   void LoseLeastSignificantDigit(); // with rounding
 
   void PushCarry(int carry) {

diff  --git a/flang/lib/Decimal/binary-to-decimal.cpp b/flang/lib/Decimal/binary-to-decimal.cpp
index a7af4036f0d6..02e39c241ee1 100644
--- a/flang/lib/Decimal/binary-to-decimal.cpp
+++ b/flang/lib/Decimal/binary-to-decimal.cpp
@@ -8,6 +8,8 @@
 
 #include "big-radix-floating-point.h"
 #include "flang/Decimal/decimal.h"
+#include <cassert>
+#include <string>
 
 namespace Fortran::decimal {
 
@@ -54,17 +56,18 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::BigRadixFloatingPointNumber(
     ++exponent_;
   }
 
+  int overflow{0};
   for (; twoPow >= 9; twoPow -= 9) {
     // D * 10.**E * 2.**twoPow -> (D*(2**9)) * 10.**E * 2.**(twoPow-9)
-    MultiplyByRounded<512>();
+    overflow |= MultiplyBy<512>();
   }
   for (; twoPow >= 3; twoPow -= 3) {
     // D * 10.**E * 2.**twoPow -> (D*(2**3)) * 10.**E * 2.**(twoPow-3)
-    MultiplyByRounded<8>();
+    overflow |= MultiplyBy<8>();
   }
   for (; twoPow > 0; --twoPow) {
     // D * 10.**E * 2.**twoPow -> (2*D) * 10.**E * 2.**(twoPow-1)
-    MultiplyByRounded<2>();
+    overflow |= MultiplyBy<2>();
   }
 
   while (twoPow < 0) {
@@ -85,21 +88,23 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::BigRadixFloatingPointNumber(
 
   for (; twoPow <= -4; twoPow += 4) {
     // D * 10.**E * 2.**twoPow -> 625D * 10.**(E-4) * 2.**(twoPow+4)
-    MultiplyByRounded<(5 * 5 * 5 * 5)>();
+    overflow |= MultiplyBy<(5 * 5 * 5 * 5)>();
     exponent_ -= 4;
   }
   if (twoPow <= -2) {
     // D * 10.**E * 2.**twoPow -> 25D * 10.**(E-2) * 2.**(twoPow+2)
-    MultiplyByRounded<25>();
+    overflow |= MultiplyBy<5 * 5>();
     twoPow += 2;
     exponent_ -= 2;
   }
   for (; twoPow < 0; ++twoPow) {
     // D * 10.**E * 2.**twoPow -> 5D * 10.**(E-1) * 2.**(twoPow+1)
-    MultiplyByRounded<5>();
+    overflow |= MultiplyBy<5>();
     --exponent_;
   }
 
+  assert(overflow == 0);
+
   // twoPow == 0, the decimal encoding is complete.
   Normalize();
 }
@@ -299,37 +304,6 @@ void BigRadixFloatingPointNumber<PREC, LOG10RADIX>::Minimize(
   Normalize();
 }
 
-template <int PREC, int LOG10RADIX>
-void BigRadixFloatingPointNumber<PREC,
-    LOG10RADIX>::LoseLeastSignificantDigit() {
-  Digit LSD{digit_[0]};
-  for (int j{0}; j < digits_ - 1; ++j) {
-    digit_[j] = digit_[j + 1];
-  }
-  digit_[digits_ - 1] = 0;
-  bool incr{false};
-  switch (rounding_) {
-  case RoundNearest:
-  case RoundDefault:
-    incr = LSD > radix / 2 || (LSD == radix / 2 && digit_[0] % 2 != 0);
-    break;
-  case RoundUp:
-    incr = LSD > 0 && !isNegative_;
-    break;
-  case RoundDown:
-    incr = LSD > 0 && isNegative_;
-    break;
-  case RoundToZero:
-    break;
-  case RoundCompatible:
-    incr = LSD >= radix / 2;
-    break;
-  }
-  for (int j{0}; (digit_[j] += incr) == radix; ++j) {
-    digit_[j] = 0;
-  }
-}
-
 template <int PREC>
 ConversionToDecimalResult ConvertToDecimal(char *buffer, std::size_t size,
     enum DecimalConversionFlags flags, int digits,
@@ -358,12 +332,13 @@ ConversionToDecimalResult ConvertToDecimal(char *buffer, std::size_t size,
       // decimal sequence in that range.
       using Binary = typename Big::Real;
       Binary less{x};
-      --less.raw;
+      less.Previous();
       Binary more{x};
       if (!x.IsMaximalFiniteMagnitude()) {
-        ++more.raw;
+        more.Next();
       }
       number.Minimize(Big{less, rounding}, Big{more, rounding});
+    } else {
     }
     return number.ConvertToDecimal(buffer, size, flags, digits);
   }
@@ -412,4 +387,22 @@ ConversionToDecimalResult ConvertLongDoubleToDecimal(char *buffer,
 }
 #endif
 }
+
+template <int PREC, int LOG10RADIX>
+llvm::raw_ostream &BigRadixFloatingPointNumber<PREC, LOG10RADIX>::Dump(
+    llvm::raw_ostream &o) const {
+  if (isNegative_) {
+    o << '-';
+  }
+  o << "10**(" << exponent_ << ") * ...\n";
+  for (int j{digits_}; --j >= 0;) {
+    std::string str{std::to_string(digit_[j])};
+    o << std::string(20 - str.size(), ' ') << str << " [" << j << ']';
+    if (j + 1 == digitLimit_) {
+      o << " (limit)";
+    }
+    o << '\n';
+  }
+  return o;
+}
 } // namespace Fortran::decimal

diff  --git a/flang/lib/Decimal/decimal-to-binary.cpp b/flang/lib/Decimal/decimal-to-binary.cpp
index f7efb230faf2..502f0a003d64 100644
--- a/flang/lib/Decimal/decimal-to-binary.cpp
+++ b/flang/lib/Decimal/decimal-to-binary.cpp
@@ -139,6 +139,37 @@ bool BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ParseNumber(
   return true;
 }
 
+template <int PREC, int LOG10RADIX>
+void BigRadixFloatingPointNumber<PREC,
+    LOG10RADIX>::LoseLeastSignificantDigit() {
+  Digit LSD{digit_[0]};
+  for (int j{0}; j < digits_ - 1; ++j) {
+    digit_[j] = digit_[j + 1];
+  }
+  digit_[digits_ - 1] = 0;
+  bool incr{false};
+  switch (rounding_) {
+  case RoundNearest:
+  case RoundDefault:
+    incr = LSD > radix / 2 || (LSD == radix / 2 && digit_[0] % 2 != 0);
+    break;
+  case RoundUp:
+    incr = LSD > 0 && !isNegative_;
+    break;
+  case RoundDown:
+    incr = LSD > 0 && isNegative_;
+    break;
+  case RoundToZero:
+    break;
+  case RoundCompatible:
+    incr = LSD >= radix / 2;
+    break;
+  }
+  for (int j{0}; (digit_[j] += incr) == radix; ++j) {
+    digit_[j] = 0;
+  }
+}
+
 // This local utility class represents an unrounded nonnegative
 // binary floating-point value with an unbiased (i.e., signed)
 // binary exponent, an integer value (not a fraction) with an implied

diff  --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp
index d505e170c437..2afa40fbfb82 100644
--- a/flang/runtime/edit-output.cpp
+++ b/flang/runtime/edit-output.cpp
@@ -396,8 +396,8 @@ bool RealOutputEditing<binaryPrecision>::Edit(const DataEdit &edit) {
   case 'B':
   case 'O':
   case 'Z':
-    return EditIntegerOutput(
-        io_, edit, decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw);
+    return EditIntegerOutput(io_, edit,
+        decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw());
   case 'G':
     return Edit(EditForGOutput(edit));
   default:


        


More information about the flang-commits mailing list