Add APFloat -> hexadecimal string conversion, as per %a and %A in C99.
authorNeil Booth <neil@daikokuya.co.uk>
Wed, 3 Oct 2007 22:26:02 +0000 (22:26 +0000)
committerNeil Booth <neil@daikokuya.co.uk>
Wed, 3 Oct 2007 22:26:02 +0000 (22:26 +0000)
Useful for diagnostics and debugging.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@42598 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/APFloat.h
lib/Support/APFloat.cpp

index c6b62dc..efd19d2 100644 (file)
 //===----------------------------------------------------------------------===//
 
 /*  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
     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
     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
 
     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;
index f5f9659..3746ae8 100644 (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