mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-05-04 18:38:05 +00:00
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:
parent
9cb7f49ee9
commit
a30b0ee959
@ -13,10 +13,10 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
/* A self-contained host- and target-independent arbitrary-precision
|
/* A self-contained host- and target-independent arbitrary-precision
|
||||||
floating-point software implementation using bignum integer
|
floating-point software implementation. It uses bignum integer
|
||||||
arithmetic, as provided by static functions in the APInt class.
|
arithmetic as provided by static functions in the APInt class.
|
||||||
The library will work with bignum integers whose parts are any
|
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
|
Written for clarity rather than speed, in particular with a view
|
||||||
to use in the front-end of a cross compiler so that target
|
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,
|
are add, subtract, multiply, divide, fused-multiply-add,
|
||||||
conversion-to-float, conversion-to-integer and
|
conversion-to-float, conversion-to-integer and
|
||||||
conversion-from-integer. New rounding modes (e.g. away from zero)
|
conversion-from-integer. New rounding modes (e.g. away from zero)
|
||||||
can be added with three or four lines of code. The library reads
|
can be added with three or four lines of code.
|
||||||
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.
|
|
||||||
|
|
||||||
Four formats are built-in: IEEE single precision, double
|
Four formats are built-in: IEEE single precision, double
|
||||||
precision, quadruple precision, and x87 80-bit extended 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
|
should be straight forward to add support for the before-rounding
|
||||||
case too.
|
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,
|
Non-zero finite numbers are represented internally as a sign bit,
|
||||||
a 16-bit signed exponent, and the significand as an array of
|
a 16-bit signed exponent, and the significand as an array of
|
||||||
integer parts. After normalization of a number of precision P the
|
integer parts. After normalization of a number of precision P the
|
||||||
@ -77,17 +85,14 @@
|
|||||||
|
|
||||||
Conversions to and from decimal strings (hard).
|
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.
|
Optional ability to detect underflow tininess before rounding.
|
||||||
|
|
||||||
New formats: x87 in single and double precision mode (IEEE apart
|
New formats: x87 in single and double precision mode (IEEE apart
|
||||||
from extended exponent range) and IBM two-double extended
|
from extended exponent range) and IBM two-double extended
|
||||||
precision (hard).
|
precision (hard).
|
||||||
|
|
||||||
New operations: sqrt, nextafter, nexttoward.
|
New operations: sqrt, IEEE remainder, C90 fmod, nextafter,
|
||||||
|
nexttoward.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LLVM_FLOAT_H
|
#ifndef LLVM_FLOAT_H
|
||||||
@ -205,6 +210,13 @@ namespace llvm {
|
|||||||
compare unordered, 0==-0). */
|
compare unordered, 0==-0). */
|
||||||
cmpResult compare(const APFloat &) const;
|
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). */
|
/* Bitwise comparison for equality (QNaNs compare equal, 0!=-0). */
|
||||||
bool bitwiseIsEqual(const APFloat &) const;
|
bool bitwiseIsEqual(const APFloat &) const;
|
||||||
|
|
||||||
@ -255,9 +267,11 @@ namespace llvm {
|
|||||||
opStatus handleOverflow(roundingMode);
|
opStatus handleOverflow(roundingMode);
|
||||||
bool roundAwayFromZero(roundingMode, lostFraction, unsigned int) const;
|
bool roundAwayFromZero(roundingMode, lostFraction, unsigned int) const;
|
||||||
opStatus convertFromUnsignedInteger(integerPart *, unsigned int,
|
opStatus convertFromUnsignedInteger(integerPart *, unsigned int,
|
||||||
roundingMode);
|
roundingMode);
|
||||||
lostFraction combineLostFractions(lostFraction, lostFraction);
|
lostFraction combineLostFractions(lostFraction, lostFraction);
|
||||||
opStatus convertFromHexadecimalString(const char *, roundingMode);
|
opStatus convertFromHexadecimalString(const char *, roundingMode);
|
||||||
|
char *convertNormalToHexString(char *, unsigned int, bool,
|
||||||
|
roundingMode) const;
|
||||||
APInt convertFloatAPFloatToAPInt() const;
|
APInt convertFloatAPFloatToAPInt() const;
|
||||||
APInt convertDoubleAPFloatToAPInt() const;
|
APInt convertDoubleAPFloatToAPInt() const;
|
||||||
APInt convertF80LongDoubleAPFloatToAPInt() const;
|
APInt convertF80LongDoubleAPFloatToAPInt() const;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
#include "llvm/ADT/APFloat.h"
|
#include "llvm/ADT/APFloat.h"
|
||||||
#include "llvm/Support/MathExtras.h"
|
#include "llvm/Support/MathExtras.h"
|
||||||
|
|
||||||
@ -20,7 +21,8 @@ using namespace llvm;
|
|||||||
|
|
||||||
#define convolve(lhs, rhs) ((lhs) * 4 + (rhs))
|
#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);
|
COMPILE_TIME_ASSERT(integerPartWidth % 4 == 0);
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
@ -187,7 +189,7 @@ namespace {
|
|||||||
/* Return the fraction lost were a bignum truncated losing the least
|
/* Return the fraction lost were a bignum truncated losing the least
|
||||||
significant BITS bits. */
|
significant BITS bits. */
|
||||||
lostFraction
|
lostFraction
|
||||||
lostFractionThroughTruncation(integerPart *parts,
|
lostFractionThroughTruncation(const integerPart *parts,
|
||||||
unsigned int partCount,
|
unsigned int partCount,
|
||||||
unsigned int bits)
|
unsigned int bits)
|
||||||
{
|
{
|
||||||
@ -219,6 +221,66 @@ namespace {
|
|||||||
|
|
||||||
return lost_fraction;
|
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. */
|
/* Constructors. */
|
||||||
@ -1167,7 +1229,7 @@ APFloat::divide(const APFloat &rhs, roundingMode rounding_mode)
|
|||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normalized remainder. */
|
/* Normalized remainder. This is not currently doing TRT. */
|
||||||
APFloat::opStatus
|
APFloat::opStatus
|
||||||
APFloat::mod(const APFloat &rhs, roundingMode rounding_mode)
|
APFloat::mod(const APFloat &rhs, roundingMode rounding_mode)
|
||||||
{
|
{
|
||||||
@ -1630,6 +1692,181 @@ APFloat::convertFromString(const char *p, roundingMode rounding_mode)
|
|||||||
abort();
|
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
|
// For good performance it is desirable for different APFloats
|
||||||
// to produce different integers.
|
// to produce different integers.
|
||||||
uint32_t
|
uint32_t
|
||||||
|
Loading…
x
Reference in New Issue
Block a user