[llvm-commits] [llvm] r42598 - in /llvm/trunk: include/llvm/ADT/APFloat.h lib/Support/APFloat.cpp
Neil Booth
neil at daikokuya.co.uk
Wed Oct 3 15:26:02 PDT 2007
Author: neil
Date: Wed Oct 3 17:26:02 2007
New Revision: 42598
URL: http://llvm.org/viewvc/llvm-project?rev=42598&view=rev
Log:
Add APFloat -> hexadecimal string conversion, as per %a and %A in C99.
Useful for diagnostics and debugging.
Modified:
llvm/trunk/include/llvm/ADT/APFloat.h
llvm/trunk/lib/Support/APFloat.cpp
Modified: llvm/trunk/include/llvm/ADT/APFloat.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/APFloat.h?rev=42598&r1=42597&r2=42598&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/APFloat.h (original)
+++ llvm/trunk/include/llvm/ADT/APFloat.h Wed Oct 3 17:26:02 2007
@@ -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 @@
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 @@
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;
Modified: llvm/trunk/lib/Support/APFloat.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/APFloat.cpp?rev=42598&r1=42597&r2=42598&view=diff
==============================================================================
--- llvm/trunk/lib/Support/APFloat.cpp (original)
+++ llvm/trunk/lib/Support/APFloat.cpp Wed Oct 3 17:26:02 2007
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include <cassert>
+#include <cstring>
#include "llvm/ADT/APFloat.h"
#include "llvm/Support/MathExtras.h"
@@ -20,7 +21,8 @@
#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 @@
/* 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 @@
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 @@
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 @@
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
More information about the llvm-commits
mailing list