Add APFloat -> hexadecimal string conversion, as per %a and %A in C99.

Useful for diagnostics and debugging.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@42598 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Neil Booth 2007-10-03 22:26:02 +00:00
parent 9cb7f49ee9
commit a30b0ee959
2 changed files with 267 additions and 16 deletions

View File

@ -13,10 +13,10 @@
//===----------------------------------------------------------------------===//
/* A self-contained host- and target-independent arbitrary-precision
floating-point software implementation using bignum integer
arithmetic, as provided by static functions in the APInt class.
floating-point software implementation. It uses bignum integer
arithmetic as provided by static functions in the APInt class.
The library will work with bignum integers whose parts are any
unsigned type at least 16 bits wide. 64 bits is recommended.
unsigned type at least 16 bits wide, but 64 bits is recommended.
Written for clarity rather than speed, in particular with a view
to use in the front-end of a cross compiler so that target
@ -30,10 +30,7 @@
are add, subtract, multiply, divide, fused-multiply-add,
conversion-to-float, conversion-to-integer and
conversion-from-integer. New rounding modes (e.g. away from zero)
can be added with three or four lines of code. The library reads
and correctly rounds hexadecimal floating point numbers as per
C99; syntax is required to have been validated by the caller.
Conversion from decimal is not currently implemented.
can be added with three or four lines of code.
Four formats are built-in: IEEE single precision, double
precision, quadruple precision, and x87 80-bit extended double
@ -54,6 +51,17 @@
should be straight forward to add support for the before-rounding
case too.
The library reads hexadecimal floating point numbers as per C99,
and correctly rounds if necessary according to the specified
rounding mode. Syntax is required to have been validated by the
caller. It also converts floating point numbers to hexadecimal
text as per the C99 %a and %A conversions. The output precision
(or alternatively the natural minimal precision) can be specified;
if the requested precision is less than the natural precision the
output is correctly rounded for the specified rounding mode.
Conversion to and from decimal text is not currently implemented.
Non-zero finite numbers are represented internally as a sign bit,
a 16-bit signed exponent, and the significand as an array of
integer parts. After normalization of a number of precision P the
@ -77,17 +85,14 @@
Conversions to and from decimal strings (hard).
Conversions to hexadecimal string.
Read and write IEEE-format in-memory representations.
Optional ability to detect underflow tininess before rounding.
New formats: x87 in single and double precision mode (IEEE apart
from extended exponent range) and IBM two-double extended
precision (hard).
New operations: sqrt, nextafter, nexttoward.
New operations: sqrt, IEEE remainder, C90 fmod, nextafter,
nexttoward.
*/
#ifndef LLVM_FLOAT_H
@ -205,6 +210,13 @@ namespace llvm {
compare unordered, 0==-0). */
cmpResult compare(const APFloat &) const;
/* Write out a hexadecimal representation of the floating point
value to DST, which must be of sufficient size, in the C99 form
[-]0xh.hhhhp[+-]d. Return the number of characters written,
excluding the terminating NUL. */
unsigned int convertToHexString(char *dst, unsigned int hexDigits,
bool upperCase, roundingMode) const;
/* Bitwise comparison for equality (QNaNs compare equal, 0!=-0). */
bool bitwiseIsEqual(const APFloat &) const;
@ -255,9 +267,11 @@ namespace llvm {
opStatus handleOverflow(roundingMode);
bool roundAwayFromZero(roundingMode, lostFraction, unsigned int) const;
opStatus convertFromUnsignedInteger(integerPart *, unsigned int,
roundingMode);
roundingMode);
lostFraction combineLostFractions(lostFraction, lostFraction);
opStatus convertFromHexadecimalString(const char *, roundingMode);
char *convertNormalToHexString(char *, unsigned int, bool,
roundingMode) const;
APInt convertFloatAPFloatToAPInt() const;
APInt convertDoubleAPFloatToAPInt() const;
APInt convertF80LongDoubleAPFloatToAPInt() const;

View File

@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include <cassert>
#include <cstring>
#include "llvm/ADT/APFloat.h"
#include "llvm/Support/MathExtras.h"
@ -20,7 +21,8 @@ using namespace llvm;
#define convolve(lhs, rhs) ((lhs) * 4 + (rhs))
/* Assumed in hexadecimal significand parsing. */
/* Assumed in hexadecimal significand parsing, and conversion to
hexadecimal strings. */
COMPILE_TIME_ASSERT(integerPartWidth % 4 == 0);
namespace llvm {
@ -187,7 +189,7 @@ namespace {
/* Return the fraction lost were a bignum truncated losing the least
significant BITS bits. */
lostFraction
lostFractionThroughTruncation(integerPart *parts,
lostFractionThroughTruncation(const integerPart *parts,
unsigned int partCount,
unsigned int bits)
{
@ -219,6 +221,66 @@ namespace {
return lost_fraction;
}
/* Zero at the end to avoid modular arithmetic when adding one; used
when rounding up during hexadecimal output. */
static const char hexDigitsLower[] = "0123456789abcdef0";
static const char hexDigitsUpper[] = "0123456789ABCDEF0";
static const char infinityL[] = "infinity";
static const char infinityU[] = "INFINITY";
static const char NaNL[] = "nan";
static const char NaNU[] = "NAN";
/* Write out an integerPart in hexadecimal, starting with the most
significant nibble. Write out exactly COUNT hexdigits, return
COUNT. */
static unsigned int
partAsHex (char *dst, integerPart part, unsigned int count,
const char *hexDigitChars)
{
unsigned int result = count;
assert (count != 0 && count <= integerPartWidth / 4);
part >>= (integerPartWidth - 4 * count);
while (count--) {
dst[count] = hexDigitChars[part & 0xf];
part >>= 4;
}
return result;
}
/* Write out a decimal exponent. */
static char *
writeDecimalExponent (char *dst, int exponent)
{
assert (exponent >= -65536 && exponent <= 65535);
if (exponent < 0) {
*dst++ = '-';
exponent = -exponent;
}
if (exponent == 0) {
*dst++ = '0';
} else {
char buff[12], *p;
p = buff;
while (exponent) {
*p++ = '0' + exponent % 10;
exponent /= 10;
}
do
*dst++ = *--p;
while (p != buff);
}
return dst;
}
}
/* Constructors. */
@ -1167,7 +1229,7 @@ APFloat::divide(const APFloat &rhs, roundingMode rounding_mode)
return fs;
}
/* Normalized remainder. */
/* Normalized remainder. This is not currently doing TRT. */
APFloat::opStatus
APFloat::mod(const APFloat &rhs, roundingMode rounding_mode)
{
@ -1630,6 +1692,181 @@ APFloat::convertFromString(const char *p, roundingMode rounding_mode)
abort();
}
/* Write out a hexadecimal representation of the floating point value
to DST, which must be of sufficient size, in the C99 form
[-]0xh.hhhhp[+-]d. Return the number of characters written,
excluding the terminating NUL.
If UPPERCASE, the output is in upper case, otherwise in lower case.
HEXDIGITS digits appear altogether, rounding the value if
necessary. If HEXDIGITS is 0, the minimal precision to display the
number precisely is used instead. If nothing would appear after
the decimal point it is suppressed.
The decimal exponent is always printed and has at least one digit.
Zero values display an exponent of zero. Infinities and NaNs
appear as "infinity" or "nan" respectively.
The above rules are as specified by C99. There is ambiguity about
what the leading hexadecimal digit should be. This implementation
uses whatever is necessary so that the exponent is displayed as
stored. This implies the exponent will fall within the IEEE format
range, and the leading hexadecimal digit will be 0 (for denormals),
1 (normal numbers) or 2 (normal numbers rounded-away-from-zero with
any other digits zero).
*/
unsigned int
APFloat::convertToHexString(char *dst, unsigned int hexDigits,
bool upperCase, roundingMode rounding_mode) const
{
char *p;
p = dst;
if (sign)
*dst++ = '-';
switch (category) {
case fcInfinity:
memcpy (dst, upperCase ? infinityU: infinityL, sizeof infinityU - 1);
dst += sizeof infinityL - 1;
break;
case fcNaN:
memcpy (dst, upperCase ? NaNU: NaNL, sizeof NaNU - 1);
dst += sizeof NaNU - 1;
break;
case fcZero:
*dst++ = '0';
*dst++ = upperCase ? 'X': 'x';
*dst++ = '0';
if (hexDigits > 1) {
*dst++ = '.';
memset (dst, '0', hexDigits - 1);
dst += hexDigits - 1;
}
*dst++ = upperCase ? 'P': 'p';
*dst++ = '0';
break;
case fcNormal:
dst = convertNormalToHexString (dst, hexDigits, upperCase, rounding_mode);
break;
}
*dst = 0;
return dst - p;
}
/* Does the hard work of outputting the correctly rounded hexadecimal
form of a normal floating point number with the specified number of
hexadecimal digits. If HEXDIGITS is zero the minimum number of
digits necessary to print the value precisely is output. */
char *
APFloat::convertNormalToHexString(char *dst, unsigned int hexDigits,
bool upperCase,
roundingMode rounding_mode) const
{
unsigned int count, valueBits, shift, partsCount, outputDigits;
const char *hexDigitChars;
const integerPart *significand;
char *p;
bool roundUp;
*dst++ = '0';
*dst++ = upperCase ? 'X': 'x';
roundUp = false;
hexDigitChars = upperCase ? hexDigitsUpper: hexDigitsLower;
significand = significandParts();
partsCount = partCount();
/* +3 because the first digit only uses the single integer bit, so
we have 3 virtual zero most-significant-bits. */
valueBits = semantics->precision + 3;
shift = integerPartWidth - valueBits % integerPartWidth;
/* The natural number of digits required ignoring trailing
insignificant zeroes. */
outputDigits = (valueBits - significandLSB () + 3) / 4;
/* hexDigits of zero means use the required number for the
precision. Otherwise, see if we are truncating. If we are,
found out if we need to round away from zero. */
if (hexDigits) {
if (hexDigits < outputDigits) {
/* We are dropping non-zero bits, so need to check how to round.
"bits" is the number of dropped bits. */
unsigned int bits;
lostFraction fraction;
bits = valueBits - hexDigits * 4;
fraction = lostFractionThroughTruncation (significand, partsCount, bits);
roundUp = roundAwayFromZero(rounding_mode, fraction, bits);
}
outputDigits = hexDigits;
}
/* Write the digits consecutively, and start writing in the location
of the hexadecimal point. We move the most significant digit
left and add the hexadecimal point later. */
p = ++dst;
count = (valueBits + integerPartWidth - 1) / integerPartWidth;
while (outputDigits && count) {
integerPart part;
/* Put the most significant integerPartWidth bits in "part". */
if (--count == partsCount)
part = 0; /* An imaginary higher zero part. */
else
part = significand[count] << shift;
if (count && shift)
part |= significand[count - 1] >> (integerPartWidth - shift);
/* Convert as much of "part" to hexdigits as we can. */
unsigned int curDigits = integerPartWidth / 4;
if (curDigits > outputDigits)
curDigits = outputDigits;
dst += partAsHex (dst, part, curDigits, hexDigitChars);
outputDigits -= curDigits;
}
if (roundUp) {
char *q = dst;
/* Note that hexDigitChars has a trailing '0'. */
do {
q--;
*q = hexDigitChars[hexDigitValue (*q) + 1];
} while (*q == '0' && q > p);
} else {
/* Add trailing zeroes. */
memset (dst, '0', outputDigits);
dst += outputDigits;
}
/* Move the most significant digit to before the point, and if there
is something after the decimal point add it. This must come
after rounding above. */
p[-1] = p[0];
if (dst -1 == p)
dst--;
else
p[0] = '.';
/* Finally output the exponent. */
*dst++ = upperCase ? 'P': 'p';
return writeDecimalExponent (dst, exponent);
}
// For good performance it is desirable for different APFloats
// to produce different integers.
uint32_t