mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-12-15 07:39:31 +00:00
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. llvm-svn: 92126
This commit is contained in:
parent
dad4e2e4e5
commit
c71c44ec7f
@ -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. */
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user