[llvm-commits] [llvm] r92126 - in /llvm/trunk: include/llvm/ADT/APFloat.h lib/Support/APFloat.cpp unittests/ADT/APFloatTest.cpp

John McCall rjmccall at apple.com
Thu Dec 24 00:56:26 PST 2009


Author: rjmccall
Date: Thu Dec 24 02:56:26 2009
New Revision: 92126

URL: http://llvm.org/viewvc/llvm-project?rev=92126&view=rev
Log:
Add accessors for the largest-magnitude, smallest-magnitude, and
smallest-normalized-magnitude values in a given FP semantics.
Provide an APFloat-to-string conversion which I am quite ready to admit could
be much more efficient.


Modified:
    llvm/trunk/include/llvm/ADT/APFloat.h
    llvm/trunk/lib/Support/APFloat.cpp
    llvm/trunk/unittests/ADT/APFloatTest.cpp

Modified: llvm/trunk/include/llvm/ADT/APFloat.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/APFloat.h?rev=92126&r1=92125&r2=92126&view=diff

==============================================================================
--- llvm/trunk/include/llvm/ADT/APFloat.h (original)
+++ llvm/trunk/include/llvm/ADT/APFloat.h Thu Dec 24 02:56:26 2009
@@ -191,6 +191,7 @@
     static APFloat getInf(const fltSemantics &Sem, bool Negative = false) {
       return APFloat(Sem, fcInfinity, Negative);
     }
+
     /// getNaN - Factory for QNaN values.
     ///
     /// \param Negative - True iff the NaN generated should be negative.
@@ -201,6 +202,26 @@
       return APFloat(Sem, fcNaN, Negative, type);
     }
 
+    /// getLargest - Returns the largest finite number in the given
+    /// semantics.
+    ///
+    /// \param Negative - True iff the number should be negative
+    static APFloat getLargest(const fltSemantics &Sem, bool Negative = false);
+
+    /// getSmallest - Returns the smallest (by magnitude) finite number
+    /// in the given semantics.  Might be denormalized, which implies a
+    /// relative loss of precision.
+    ///
+    /// \param Negative - True iff the number should be negative
+    static APFloat getSmallest(const fltSemantics &Sem, bool Negative = false);
+
+    /// getSmallestNormalized - Returns the smallest (by magnitude)
+    /// normalized finite number in the given semantics.
+    ///
+    /// \param Negative - True iff the number should be negative
+    static APFloat getSmallestNormalized(const fltSemantics &Sem,
+                                         bool Negative = false);
+
     /// Profile - Used to insert APFloat objects, or objects that contain
     ///  APFloat objects, into FoldingSets.
     void Profile(FoldingSetNodeID& NID) const;
@@ -277,6 +298,29 @@
     /* Return an arbitrary integer value usable for hashing. */
     uint32_t getHashValue() const;
 
+    /// Converts this value into a decimal string.
+    ///
+    /// \param FormatPrecision The maximum number of digits of
+    ///   precision to output.  If there are fewer digits available,
+    ///   zero padding will not be used unless the value is
+    ///   integral and small enough to be expressed in
+    ///   FormatPrecision digits.
+    /// \param FormatMaxPadding The maximum number of zeros to
+    ///   consider inserting before falling back to scientific
+    ///   notation.  0 means to always use scientific notation.
+    ///
+    /// Number       Precision    MaxPadding      Result
+    /// ------       ---------    ----------      ------
+    /// 1.01E+4              5             2       10100
+    /// 1.01E+4              4             2       1.01E+4
+    /// 1.01E+4              5             1       1.01E+4
+    /// 1.01E-2              5             2       0.0101
+    /// 1.01E-2              4             2       1.01E-2
+    /// 1.01E-2              4             1       1.01E-2
+    void toString(SmallVectorImpl<char> &Str,
+                  unsigned FormatPrecision = 8,
+                  unsigned FormatMaxPadding = 3);
+
   private:
 
     /* Trivial queries.  */

Modified: llvm/trunk/lib/Support/APFloat.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/APFloat.cpp?rev=92126&r1=92125&r2=92126&view=diff

==============================================================================
--- llvm/trunk/lib/Support/APFloat.cpp (original)
+++ llvm/trunk/lib/Support/APFloat.cpp Thu Dec 24 02:56:26 2009
@@ -3139,6 +3139,60 @@
     llvm_unreachable(0);
 }
 
+APFloat APFloat::getLargest(const fltSemantics &Sem, bool Negative) {
+  APFloat Val(Sem, fcNormal, Negative);
+
+  // We want (in interchange format):
+  //   sign = {Negative}
+  //   exponent = 1..10
+  //   significand = 1..1
+
+  Val.exponent = Sem.maxExponent; // unbiased
+
+  // 1-initialize all bits....
+  Val.zeroSignificand();
+  integerPart *significand = Val.significandParts();
+  unsigned N = partCountForBits(Sem.precision);
+  for (unsigned i = 0; i != N; ++i)
+    significand[i] = ~((integerPart) 0);
+
+  // ...and then clear the top bits for internal consistency.
+  significand[N-1]
+    &= (((integerPart) 1) << ((Sem.precision % integerPartWidth) - 1)) - 1;
+
+  return Val;
+}
+
+APFloat APFloat::getSmallest(const fltSemantics &Sem, bool Negative) {
+  APFloat Val(Sem, fcNormal, Negative);
+
+  // We want (in interchange format):
+  //   sign = {Negative}
+  //   exponent = 0..0
+  //   significand = 0..01
+
+  Val.exponent = Sem.minExponent; // unbiased
+  Val.zeroSignificand();
+  Val.significandParts()[0] = 1;
+  return Val;
+}
+
+APFloat APFloat::getSmallestNormalized(const fltSemantics &Sem, bool Negative) {
+  APFloat Val(Sem, fcNormal, Negative);
+
+  // We want (in interchange format):
+  //   sign = {Negative}
+  //   exponent = 0..0
+  //   significand = 10..0
+
+  Val.exponent = Sem.minExponent;
+  Val.zeroSignificand();
+  Val.significandParts()[partCountForBits(Sem.precision)-1]
+    |= (((integerPart) 1) << ((Sem.precision % integerPartWidth) - 1));
+
+  return Val;
+}
+
 APFloat::APFloat(const APInt& api, bool isIEEE)
 {
   initFromAPInt(api, isIEEE);
@@ -3155,3 +3209,250 @@
   APInt api = APInt(64, 0);
   initFromAPInt(api.doubleToBits(d));
 }
+
+namespace {
+  static void append(SmallVectorImpl<char> &Buffer,
+                     unsigned N, const char *Str) {
+    unsigned Start = Buffer.size();
+    Buffer.set_size(Start + N);
+    memcpy(&Buffer[Start], Str, N);
+  }
+
+  template <unsigned N>
+  void append(SmallVectorImpl<char> &Buffer, const char (&Str)[N]) {
+    append(Buffer, N, Str);
+  }
+
+  void AdjustToPrecision(SmallVectorImpl<char> &buffer,
+                         int &exp, unsigned FormatPrecision) {
+    unsigned N = buffer.size();
+    if (N <= FormatPrecision) return;
+
+    // The most significant figures are the last ones in the buffer.
+    unsigned FirstSignificant = N - FormatPrecision;
+
+    // Round.
+    // FIXME: this probably shouldn't use 'round half up'.
+
+    // Rounding down is just a truncation, except we also want to drop
+    // trailing zeros from the new result.
+    if (buffer[FirstSignificant - 1] < '5') {
+      while (buffer[FirstSignificant] == '0')
+        FirstSignificant++;
+
+      exp += FirstSignificant;
+      buffer.erase(&buffer[0], &buffer[FirstSignificant]);
+      return;
+    }
+
+    // Rounding up requires a decimal add-with-carry.  If we continue
+    // the carry, the newly-introduced zeros will just be truncated.
+    for (unsigned I = FirstSignificant; I != N; ++I) {
+      if (buffer[I] == '9') {
+        FirstSignificant++;
+      } else {
+        buffer[I]++;
+        break;
+      }
+    }
+
+    // If we carried through, we have exactly one digit of precision.
+    if (FirstSignificant == N) {
+      exp += FirstSignificant;
+      buffer.clear();
+      buffer.push_back('1');
+      return;
+    }
+
+    exp += FirstSignificant;
+    buffer.erase(&buffer[0], &buffer[FirstSignificant]);
+  }
+}
+
+void APFloat::toString(SmallVectorImpl<char> &Str,
+                       unsigned FormatPrecision,
+                       unsigned FormatMaxPadding) {
+  switch (category) {
+  case fcInfinity:
+    if (isNegative())
+      return append(Str, "-Inf");
+    else
+      return append(Str, "+Inf");
+
+  case fcNaN: return append(Str, "NaN");
+
+  case fcZero:
+    if (isNegative())
+      Str.push_back('-');
+
+    if (!FormatMaxPadding)
+      append(Str, "0.0E+0");
+    else
+      Str.push_back('0');
+    return;
+
+  case fcNormal:
+    break;
+  }
+
+  if (isNegative())
+    Str.push_back('-');
+
+  // Decompose the number into an APInt and an exponent.
+  int exp = exponent - ((int) semantics->precision - 1);
+  APInt significand(semantics->precision,
+                    partCountForBits(semantics->precision),
+                    significandParts());
+
+  // Ignore trailing binary zeros.
+  int trailingZeros = significand.countTrailingZeros();
+  exp += trailingZeros;
+  significand = significand.lshr(trailingZeros);
+
+  // Change the exponent from 2^e to 10^e.
+  if (exp == 0) {
+    // Nothing to do.
+  } else if (exp > 0) {
+    // Just shift left.
+    significand.zext(semantics->precision + exp);
+    significand <<= exp;
+    exp = 0;
+  } else { /* exp < 0 */
+    int texp = -exp;
+
+    // We transform this using the identity:
+    //   (N)(2^-e) == (N)(5^e)(10^-e)
+    // This means we have to multiply N (the significand) by 5^e.
+    // To avoid overflow, we have to operate on numbers large
+    // enough to store N * 5^e:
+    //   log2(N * 5^e) == log2(N) + e * log2(5)
+    //                 <= semantics->precision + e * 2.5
+    //   (log_2(5) ~ 2.321928)
+    unsigned precision = semantics->precision + 5 * texp / 2;
+
+    // Multiply significand by 5^e.
+    //   N * 5^0101 == N * 5^(1*1) * 5^(0*2) * 5^(1*4) * 5^(0*8)
+    significand.zext(precision);
+    APInt five_to_the_i(precision, 5);
+    while (true) {
+      if (texp & 1) significand *= five_to_the_i;
+      
+      texp >>= 1;
+      if (!texp) break;
+      five_to_the_i *= five_to_the_i;
+    }
+  }
+
+  llvm::SmallVector<char, 256> buffer;
+
+  // Fill the buffer.
+  unsigned precision = significand.getBitWidth();
+  APInt ten(precision, 10);
+  APInt digit(precision, 0);
+
+  bool inTrail = true;
+  while (significand != 0) {
+    // digit <- significand % 10
+    // significand <- significand / 10
+    APInt::udivrem(significand, ten, significand, digit);
+
+    unsigned d = digit.getZExtValue();
+
+    // Drop trailing zeros.
+    if (inTrail && !d) exp++;
+    else {
+      buffer.push_back((char) ('0' + d));
+      inTrail = false;
+    }
+  }
+
+  assert(!buffer.empty() && "no characters in buffer!");
+
+  // Drop down to FormatPrecision.
+  // TODO: don't do more precise calculations above than are required.
+  AdjustToPrecision(buffer, exp, FormatPrecision);
+
+  unsigned NDigits = buffer.size();
+
+  // Check whether we should a non-scientific format.
+  bool FormatScientific;
+  if (!FormatMaxPadding)
+    FormatScientific = true;
+  else {
+    unsigned Padding;
+    if (exp >= 0) {
+      // 765e3 == 765000
+      //             ^^^
+      Padding = (unsigned) exp;
+    } else {
+      unsigned Margin = (unsigned) -exp;
+      if (Margin < NDigits) {
+        // 765e-2 == 7.65
+        Padding = 0;
+      } else {
+        // 765e-5 == 0.00765
+        //           ^ ^^
+        Padding = Margin + 1 - NDigits;
+      }
+    }
+
+    FormatScientific = (Padding > FormatMaxPadding ||
+                        Padding + NDigits > FormatPrecision);
+  }
+
+  // Scientific formatting is pretty straightforward.
+  if (FormatScientific) {
+    exp += (NDigits - 1);
+
+    Str.push_back(buffer[NDigits-1]);
+    Str.push_back('.');
+    if (NDigits == 1)
+      Str.push_back('0');
+    else
+      for (unsigned I = 1; I != NDigits; ++I)
+        Str.push_back(buffer[NDigits-1-I]);
+    Str.push_back('E');
+
+    Str.push_back(exp >= 0 ? '+' : '-');
+    if (exp < 0) exp = -exp;
+    SmallVector<char, 6> expbuf;
+    do {
+      expbuf.push_back((char) ('0' + (exp % 10)));
+      exp /= 10;
+    } while (exp);
+    for (unsigned I = 0, E = expbuf.size(); I != E; ++I)
+      Str.push_back(expbuf[E-1-I]);
+    return;
+  }
+
+  // Non-scientific, positive exponents.
+  if (exp >= 0) {
+    for (unsigned I = 0; I != NDigits; ++I)
+      Str.push_back(buffer[NDigits-1-I]);
+    for (unsigned I = 0; I != (unsigned) exp; ++I)
+      Str.push_back('0');
+    return;
+  }
+
+  // Non-scientific, negative exponents.
+
+  // The number of digits to the left of the decimal point.
+  int NWholeDigits = exp + (int) NDigits;
+
+  unsigned I = 0;
+  if (NWholeDigits > 0) {
+    for (; I != (unsigned) NWholeDigits; ++I)
+      Str.push_back(buffer[NDigits-I-1]);
+    Str.push_back('.');
+  } else {
+    unsigned NZeros = 1 + (unsigned) -NWholeDigits;
+
+    Str.push_back('0');
+    Str.push_back('.');
+    for (unsigned Z = 1; Z != NZeros; ++Z)
+      Str.push_back('0');
+  }
+
+  for (; I != NDigits; ++I)
+    Str.push_back(buffer[NDigits-I-1]);
+}

Modified: llvm/trunk/unittests/ADT/APFloatTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/APFloatTest.cpp?rev=92126&r1=92125&r2=92126&view=diff

==============================================================================
--- llvm/trunk/unittests/ADT/APFloatTest.cpp (original)
+++ llvm/trunk/unittests/ADT/APFloatTest.cpp Thu Dec 24 02:56:26 2009
@@ -8,10 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 #include <ostream>
+#include <string>
 #include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace llvm;
 
@@ -21,6 +23,13 @@
   return F.convertToDouble();
 }
 
+static std::string convertToString(double d, unsigned Prec, unsigned Pad) {
+  llvm::SmallVector<char, 100> Buffer;
+  llvm::APFloat F(d);
+  F.toString(Buffer, Prec, Pad);
+  return std::string(Buffer.data(), Buffer.size());
+}
+
 namespace {
 
 TEST(APFloatTest, Zero) {
@@ -313,6 +322,17 @@
   EXPECT_EQ(2.71828, convertToDoubleFromString("2.71828"));
 }
 
+TEST(APFloatTest, toString) {
+  ASSERT_EQ("10", convertToString(10.0, 6, 3));
+  ASSERT_EQ("1.0E+1", convertToString(10.0, 6, 0));
+  ASSERT_EQ("10100", convertToString(1.01E+4, 5, 2));
+  ASSERT_EQ("1.01E+4", convertToString(1.01E+4, 4, 2));
+  ASSERT_EQ("1.01E+4", convertToString(1.01E+4, 5, 1));
+  ASSERT_EQ("0.0101", convertToString(1.01E-2, 5, 2));
+  ASSERT_EQ("1.01E-2", convertToString(1.01E-2, 4, 2));
+  ASSERT_EQ("1.01E-2", convertToString(1.01E-2, 5, 1));
+}
+
 #ifdef GTEST_HAS_DEATH_TEST
 TEST(APFloatTest, SemanticsDeath) {
   EXPECT_DEATH(APFloat(APFloat::IEEEsingle, 0.0f).convertToDouble(), "Float semantics are not IEEEdouble");





More information about the llvm-commits mailing list