aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2009-12-24 08:56:26 +0000
committerJohn McCall <rjmccall@apple.com>2009-12-24 08:56:26 +0000
commit00e65de9d83f846af732e3ace3f48d43d67b13d1 (patch)
tree42c408ec6150bfa417d15fa1bf930cafa6d01f65
parentd73bf5987a0855e677fcefba74334c6a85b0b326 (diff)
downloadexternal_llvm-00e65de9d83f846af732e3ace3f48d43d67b13d1.zip
external_llvm-00e65de9d83f846af732e3ace3f48d43d67b13d1.tar.gz
external_llvm-00e65de9d83f846af732e3ace3f48d43d67b13d1.tar.bz2
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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@92126 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/ADT/APFloat.h44
-rw-r--r--lib/Support/APFloat.cpp301
-rw-r--r--unittests/ADT/APFloatTest.cpp20
3 files changed, 365 insertions, 0 deletions
diff --git a/include/llvm/ADT/APFloat.h b/include/llvm/ADT/APFloat.h
index 30d998f..d4c2f95 100644
--- a/include/llvm/ADT/APFloat.h
+++ b/include/llvm/ADT/APFloat.h
@@ -191,6 +191,7 @@ namespace llvm {
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 @@ namespace llvm {
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 @@ namespace llvm {
/* 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. */
diff --git a/lib/Support/APFloat.cpp b/lib/Support/APFloat.cpp
index b9b323c..4426a36 100644
--- a/lib/Support/APFloat.cpp
+++ b/lib/Support/APFloat.cpp
@@ -3139,6 +3139,60 @@ APFloat::initFromAPInt(const APInt& api, bool isIEEE)
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 @@ APFloat::APFloat(double d)
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]);
+}
diff --git a/unittests/ADT/APFloatTest.cpp b/unittests/ADT/APFloatTest.cpp
index 92f020b..d2a977a 100644
--- a/unittests/ADT/APFloatTest.cpp
+++ b/unittests/ADT/APFloatTest.cpp
@@ -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 @@ static double convertToDoubleFromString(const char *Str) {
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 @@ TEST(APFloatTest, fromHexadecimalString) {
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");