From: Neil Booth Date: Wed, 3 Oct 2007 22:26:02 +0000 (+0000) Subject: Add APFloat -> hexadecimal string conversion, as per %a and %A in C99. X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=a30b0ee959b53e83ed3697ee0b704a493829dc04 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 --- diff --git a/include/llvm/ADT/APFloat.h b/include/llvm/ADT/APFloat.h index c6b62dc7f8c..efd19d2c562 100644 --- a/include/llvm/ADT/APFloat.h +++ b/include/llvm/ADT/APFloat.h @@ -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; diff --git a/lib/Support/APFloat.cpp b/lib/Support/APFloat.cpp index f5f9659b3c4..3746ae8cfef 100644 --- a/lib/Support/APFloat.cpp +++ b/lib/Support/APFloat.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include +#include #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