]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/target-float.c
Update copyright year range in all GDB files
[thirdparty/binutils-gdb.git] / gdb / target-float.c
index e90cf03786803e1f24d5bd64a6b2bbb4f2925c65..e287ace27aabe26c9483d70614f352c1cd9ffac1 100644 (file)
@@ -1,6 +1,6 @@
 /* Floating point routines for GDB, the GNU debugger.
 
-   Copyright (C) 2017 Free Software Foundation, Inc.
+   Copyright (C) 2017-2021 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
-#include "dfp.h"
-#include "doublest.h"
 #include "gdbtypes.h"
 #include "floatformat.h"
 #include "target-float.h"
+#include "gdbarch.h"
+
+/* Target floating-point operations.
+
+   We provide multiple implementations of those operations, which differ
+   by the host-side intermediate format they perform computations in.
+
+   Those multiple implementations all derive from the following abstract
+   base class, which specifies the set of operations to be implemented.  */
+
+class target_float_ops
+{
+public:
+  virtual std::string to_string (const gdb_byte *addr, const struct type *type,
+                                const char *format) const = 0;
+  virtual bool from_string (gdb_byte *addr, const struct type *type,
+                           const std::string &string) const = 0;
+
+  virtual LONGEST to_longest (const gdb_byte *addr,
+                             const struct type *type) const = 0;
+  virtual void from_longest (gdb_byte *addr, const struct type *type,
+                            LONGEST val) const = 0;
+  virtual void from_ulongest (gdb_byte *addr, const struct type *type,
+                             ULONGEST val) const = 0;
+  virtual double to_host_double (const gdb_byte *addr,
+                                const struct type *type) const = 0;
+  virtual void from_host_double (gdb_byte *addr, const struct type *type,
+                                double val) const = 0;
+  virtual void convert (const gdb_byte *from, const struct type *from_type,
+                       gdb_byte *to, const struct type *to_type) const = 0;
+
+  virtual void binop (enum exp_opcode opcode,
+                     const gdb_byte *x, const struct type *type_x,
+                     const gdb_byte *y, const struct type *type_y,
+                     gdb_byte *res, const struct type *type_res) const = 0;
+  virtual int compare (const gdb_byte *x, const struct type *type_x,
+                      const gdb_byte *y, const struct type *type_y) const = 0;
+};
+
+
+/* Helper routines operating on binary floating-point data.  */
+
+#include <cmath>
+#include <limits>
+
+/* Different kinds of floatformat numbers recognized by
+   floatformat_classify.  To avoid portability issues, we use local
+   values instead of the C99 macros (FP_NAN et cetera).  */
+enum float_kind {
+  float_nan,
+  float_infinite,
+  float_zero,
+  float_normal,
+  float_subnormal
+};
+
+/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
+   going to bother with trying to muck around with whether it is defined in
+   a system header, what we do if not, etc.  */
+#define FLOATFORMAT_CHAR_BIT 8
+
+/* The number of bytes that the largest floating-point type that we
+   can convert to doublest will need.  */
+#define FLOATFORMAT_LARGEST_BYTES 16
+
+/* Return the floatformat's total size in host bytes.  */
+static size_t
+floatformat_totalsize_bytes (const struct floatformat *fmt)
+{
+  return ((fmt->totalsize + FLOATFORMAT_CHAR_BIT - 1)
+         / FLOATFORMAT_CHAR_BIT);
+}
+
+/* Return the precision of the floating point format FMT.  */
+static int
+floatformat_precision (const struct floatformat *fmt)
+{
+  /* Assume the precision of and IBM long double is twice the precision
+     of the underlying double.  This matches what GCC does.  */
+  if (fmt->split_half)
+    return 2 * floatformat_precision (fmt->split_half);
+
+  /* Otherwise, the precision is the size of mantissa in bits,
+     including the implicit bit if present.  */
+  int prec = fmt->man_len;
+  if (fmt->intbit == floatformat_intbit_no)
+    prec++;
+
+  return prec;
+}
+
+/* Normalize the byte order of FROM into TO.  If no normalization is
+   needed then FMT->byteorder is returned and TO is not changed;
+   otherwise the format of the normalized form in TO is returned.  */
+static enum floatformat_byteorders
+floatformat_normalize_byteorder (const struct floatformat *fmt,
+                                const void *from, void *to)
+{
+  const unsigned char *swapin;
+  unsigned char *swapout;
+  int words;
+
+  if (fmt->byteorder == floatformat_little
+      || fmt->byteorder == floatformat_big)
+    return fmt->byteorder;
+
+  words = fmt->totalsize / FLOATFORMAT_CHAR_BIT;
+  words >>= 2;
+
+  swapout = (unsigned char *)to;
+  swapin = (const unsigned char *)from;
+
+  if (fmt->byteorder == floatformat_vax)
+    {
+      while (words-- > 0)
+       {
+         *swapout++ = swapin[1];
+         *swapout++ = swapin[0];
+         *swapout++ = swapin[3];
+         *swapout++ = swapin[2];
+         swapin += 4;
+       }
+      /* This may look weird, since VAX is little-endian, but it is
+        easier to translate to big-endian than to little-endian.  */
+      return floatformat_big;
+    }
+  else
+    {
+      gdb_assert (fmt->byteorder == floatformat_littlebyte_bigword);
+
+      while (words-- > 0)
+       {
+         *swapout++ = swapin[3];
+         *swapout++ = swapin[2];
+         *swapout++ = swapin[1];
+         *swapout++ = swapin[0];
+         swapin += 4;
+       }
+      return floatformat_big;
+    }
+}
+
+/* Extract a field which starts at START and is LEN bytes long.  DATA and
+   TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER.  */
+static unsigned long
+get_field (const bfd_byte *data, enum floatformat_byteorders order,
+          unsigned int total_len, unsigned int start, unsigned int len)
+{
+  unsigned long result;
+  unsigned int cur_byte;
+  int cur_bitshift;
+
+  /* Caller must byte-swap words before calling this routine.  */
+  gdb_assert (order == floatformat_little || order == floatformat_big);
+
+  /* Start at the least significant part of the field.  */
+  if (order == floatformat_little)
+    {
+      /* We start counting from the other end (i.e, from the high bytes
+        rather than the low bytes).  As such, we need to be concerned
+        with what happens if bit 0 doesn't start on a byte boundary.
+        I.e, we need to properly handle the case where total_len is
+        not evenly divisible by 8.  So we compute ``excess'' which
+        represents the number of bits from the end of our starting
+        byte needed to get to bit 0.  */
+      int excess = FLOATFORMAT_CHAR_BIT - (total_len % FLOATFORMAT_CHAR_BIT);
+
+      cur_byte = (total_len / FLOATFORMAT_CHAR_BIT)
+                - ((start + len + excess) / FLOATFORMAT_CHAR_BIT);
+      cur_bitshift = ((start + len + excess) % FLOATFORMAT_CHAR_BIT)
+                    - FLOATFORMAT_CHAR_BIT;
+    }
+  else
+    {
+      cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
+      cur_bitshift =
+       ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
+    }
+  if (cur_bitshift > -FLOATFORMAT_CHAR_BIT)
+    result = *(data + cur_byte) >> (-cur_bitshift);
+  else
+    result = 0;
+  cur_bitshift += FLOATFORMAT_CHAR_BIT;
+  if (order == floatformat_little)
+    ++cur_byte;
+  else
+    --cur_byte;
+
+  /* Move towards the most significant part of the field.  */
+  while (cur_bitshift < len)
+    {
+      result |= (unsigned long)*(data + cur_byte) << cur_bitshift;
+      cur_bitshift += FLOATFORMAT_CHAR_BIT;
+      switch (order)
+       {
+       case floatformat_little:
+         ++cur_byte;
+         break;
+       case floatformat_big:
+         --cur_byte;
+         break;
+       }
+    }
+  if (len < sizeof(result) * FLOATFORMAT_CHAR_BIT)
+    /* Mask out bits which are not part of the field.  */
+    result &= ((1UL << len) - 1);
+  return result;
+}
+
+/* Set a field which starts at START and is LEN bytes long.  DATA and
+   TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER.  */
+static void
+put_field (unsigned char *data, enum floatformat_byteorders order,
+          unsigned int total_len, unsigned int start, unsigned int len,
+          unsigned long stuff_to_put)
+{
+  unsigned int cur_byte;
+  int cur_bitshift;
+
+  /* Caller must byte-swap words before calling this routine.  */
+  gdb_assert (order == floatformat_little || order == floatformat_big);
+
+  /* Start at the least significant part of the field.  */
+  if (order == floatformat_little)
+    {
+      int excess = FLOATFORMAT_CHAR_BIT - (total_len % FLOATFORMAT_CHAR_BIT);
+
+      cur_byte = (total_len / FLOATFORMAT_CHAR_BIT)
+                - ((start + len + excess) / FLOATFORMAT_CHAR_BIT);
+      cur_bitshift = ((start + len + excess) % FLOATFORMAT_CHAR_BIT)
+                    - FLOATFORMAT_CHAR_BIT;
+    }
+  else
+    {
+      cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
+      cur_bitshift =
+       ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
+    }
+  if (cur_bitshift > -FLOATFORMAT_CHAR_BIT)
+    {
+      *(data + cur_byte) &=
+       ~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1)
+         << (-cur_bitshift));
+      *(data + cur_byte) |=
+       (stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift);
+    }
+  cur_bitshift += FLOATFORMAT_CHAR_BIT;
+  if (order == floatformat_little)
+    ++cur_byte;
+  else
+    --cur_byte;
+
+  /* Move towards the most significant part of the field.  */
+  while (cur_bitshift < len)
+    {
+      if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
+       {
+         /* This is the last byte.  */
+         *(data + cur_byte) &=
+           ~((1 << (len - cur_bitshift)) - 1);
+         *(data + cur_byte) |= (stuff_to_put >> cur_bitshift);
+       }
+      else
+       *(data + cur_byte) = ((stuff_to_put >> cur_bitshift)
+                             & ((1 << FLOATFORMAT_CHAR_BIT) - 1));
+      cur_bitshift += FLOATFORMAT_CHAR_BIT;
+      if (order == floatformat_little)
+       ++cur_byte;
+      else
+       --cur_byte;
+    }
+}
+
+/* Check if VAL (which is assumed to be a floating point number whose
+   format is described by FMT) is negative.  */
+static int
+floatformat_is_negative (const struct floatformat *fmt,
+                        const bfd_byte *uval)
+{
+  enum floatformat_byteorders order;
+  unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
+
+  gdb_assert (fmt != NULL);
+  gdb_assert (fmt->totalsize
+             <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
+
+  /* An IBM long double (a two element array of double) always takes the
+     sign of the first double.  */
+  if (fmt->split_half)
+    fmt = fmt->split_half;
+
+  order = floatformat_normalize_byteorder (fmt, uval, newfrom);
+
+  if (order != fmt->byteorder)
+    uval = newfrom;
+
+  return get_field (uval, order, fmt->totalsize, fmt->sign_start, 1);
+}
+
+/* Check if VAL is "not a number" (NaN) for FMT.  */
+static enum float_kind
+floatformat_classify (const struct floatformat *fmt,
+                     const bfd_byte *uval)
+{
+  long exponent;
+  unsigned long mant;
+  unsigned int mant_bits, mant_off;
+  int mant_bits_left;
+  enum floatformat_byteorders order;
+  unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
+  int mant_zero;
+
+  gdb_assert (fmt != NULL);
+  gdb_assert (fmt->totalsize
+             <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
+
+  /* An IBM long double (a two element array of double) can be classified
+     by looking at the first double.  inf and nan are specified as
+     ignoring the second double.  zero and subnormal will always have
+     the second double 0.0 if the long double is correctly rounded.  */
+  if (fmt->split_half)
+    fmt = fmt->split_half;
+
+  order = floatformat_normalize_byteorder (fmt, uval, newfrom);
+
+  if (order != fmt->byteorder)
+    uval = newfrom;
+
+  exponent = get_field (uval, order, fmt->totalsize, fmt->exp_start,
+                       fmt->exp_len);
+
+  mant_bits_left = fmt->man_len;
+  mant_off = fmt->man_start;
+
+  mant_zero = 1;
+  while (mant_bits_left > 0)
+    {
+      mant_bits = std::min (mant_bits_left, 32);
+
+      mant = get_field (uval, order, fmt->totalsize, mant_off, mant_bits);
+
+      /* If there is an explicit integer bit, mask it off.  */
+      if (mant_off == fmt->man_start
+         && fmt->intbit == floatformat_intbit_yes)
+       mant &= ~(1 << (mant_bits - 1));
+
+      if (mant)
+       {
+         mant_zero = 0;
+         break;
+       }
+
+      mant_off += mant_bits;
+      mant_bits_left -= mant_bits;
+    }
+
+  /* If exp_nan is not set, assume that inf, NaN, and subnormals are not
+     supported.  */
+  if (! fmt->exp_nan)
+    {
+      if (mant_zero)
+       return float_zero;
+      else
+       return float_normal;
+    }
+
+  if (exponent == 0)
+    {
+      if (mant_zero)
+       return float_zero;
+      else
+       return float_subnormal;
+    }
+
+  if (exponent == fmt->exp_nan)
+    {
+      if (mant_zero)
+       return float_infinite;
+      else
+       return float_nan;
+    }
+
+  return float_normal;
+}
+
+/* Convert the mantissa of VAL (which is assumed to be a floating
+   point number whose format is described by FMT) into a hexadecimal
+   and store it in a static string.  Return a pointer to that string.  */
+static const char *
+floatformat_mantissa (const struct floatformat *fmt,
+                     const bfd_byte *val)
+{
+  unsigned char *uval = (unsigned char *) val;
+  unsigned long mant;
+  unsigned int mant_bits, mant_off;
+  int mant_bits_left;
+  static char res[50];
+  char buf[9];
+  int len;
+  enum floatformat_byteorders order;
+  unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
+
+  gdb_assert (fmt != NULL);
+  gdb_assert (fmt->totalsize
+             <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
+
+  /* For IBM long double (a two element array of double), return the
+     mantissa of the first double.  The problem with returning the
+     actual mantissa from both doubles is that there can be an
+     arbitrary number of implied 0's or 1's between the mantissas
+     of the first and second double.  In any case, this function
+     is only used for dumping out nans, and a nan is specified to
+     ignore the value in the second double.  */
+  if (fmt->split_half)
+    fmt = fmt->split_half;
+
+  order = floatformat_normalize_byteorder (fmt, uval, newfrom);
+
+  if (order != fmt->byteorder)
+    uval = newfrom;
+
+  if (! fmt->exp_nan)
+    return 0;
+
+  /* Make sure we have enough room to store the mantissa.  */
+  gdb_assert (sizeof res > ((fmt->man_len + 7) / 8) * 2);
+
+  mant_off = fmt->man_start;
+  mant_bits_left = fmt->man_len;
+  mant_bits = (mant_bits_left % 32) > 0 ? mant_bits_left % 32 : 32;
+
+  mant = get_field (uval, order, fmt->totalsize, mant_off, mant_bits);
+
+  len = xsnprintf (res, sizeof res, "%lx", mant);
+
+  mant_off += mant_bits;
+  mant_bits_left -= mant_bits;
+
+  while (mant_bits_left > 0)
+    {
+      mant = get_field (uval, order, fmt->totalsize, mant_off, 32);
+
+      xsnprintf (buf, sizeof buf, "%08lx", mant);
+      gdb_assert (len + strlen (buf) <= sizeof res);
+      strcat (res, buf);
+
+      mant_off += 32;
+      mant_bits_left -= 32;
+    }
+
+  return res;
+}
+
+/* Convert printf format string FORMAT to the otherwise equivalent string
+   which may be used to print a host floating-point number using the length
+   modifier LENGTH (which may be 0 if none is needed).  If FORMAT is null,
+   return a format appropriate to print the full precision of a target
+   floating-point number of format FMT.  */
+static std::string
+floatformat_printf_format (const struct floatformat *fmt,
+                          const char *format, char length)
+{
+  std::string host_format;
+  char conversion;
+
+  if (format == nullptr)
+    {
+      /* If no format was specified, print the number using a format string
+        where the precision is set to the DECIMAL_DIG value for the given
+        floating-point format.  This value is computed as
+
+               ceil(1 + p * log10(b)),
+
+        where p is the precision of the floating-point format in bits, and
+        b is the base (which is always 2 for the formats we support).  */
+      const double log10_2 = .30102999566398119521;
+      double d_decimal_dig = 1 + floatformat_precision (fmt) * log10_2;
+      int decimal_dig = d_decimal_dig;
+      if (decimal_dig < d_decimal_dig)
+       decimal_dig++;
+
+      host_format = string_printf ("%%.%d", decimal_dig);
+      conversion = 'g';
+    }
+  else
+    {
+      /* Use the specified format, stripping out the conversion character
+        and length modifier, if present.  */
+      size_t len = strlen (format);
+      gdb_assert (len > 1);
+      conversion = format[--len];
+      gdb_assert (conversion == 'e' || conversion == 'f' || conversion == 'g'
+                 || conversion == 'E' || conversion == 'G');
+      if (format[len - 1] == 'L')
+       len--;
+
+      host_format = std::string (format, len);
+    }
+
+  /* Add the length modifier and conversion character appropriate for
+     handling the appropriate host floating-point type.  */
+  if (length)
+    host_format += length;
+  host_format += conversion;
+
+  return host_format;
+}
+
+/* Implementation of target_float_ops using the host floating-point type T
+   as intermediate type.  */
+
+template<typename T> class host_float_ops : public target_float_ops
+{
+public:
+  std::string to_string (const gdb_byte *addr, const struct type *type,
+                        const char *format) const override;
+  bool from_string (gdb_byte *addr, const struct type *type,
+                   const std::string &string) const override;
+
+  LONGEST to_longest (const gdb_byte *addr,
+                     const struct type *type) const override;
+  void from_longest (gdb_byte *addr, const struct type *type,
+                    LONGEST val) const override;
+  void from_ulongest (gdb_byte *addr, const struct type *type,
+                     ULONGEST val) const override;
+  double to_host_double (const gdb_byte *addr,
+                        const struct type *type) const override;
+  void from_host_double (gdb_byte *addr, const struct type *type,
+                        double val) const override;
+  void convert (const gdb_byte *from, const struct type *from_type,
+               gdb_byte *to, const struct type *to_type) const override;
+
+  void binop (enum exp_opcode opcode,
+             const gdb_byte *x, const struct type *type_x,
+             const gdb_byte *y, const struct type *type_y,
+             gdb_byte *res, const struct type *type_res) const override;
+  int compare (const gdb_byte *x, const struct type *type_x,
+              const gdb_byte *y, const struct type *type_y) const override;
+
+private:
+  void from_target (const struct floatformat *fmt,
+                   const gdb_byte *from, T *to) const;
+  void from_target (const struct type *type,
+                   const gdb_byte *from, T *to) const;
+
+  void to_target (const struct type *type,
+                 const T *from, gdb_byte *to) const;
+  void to_target (const struct floatformat *fmt,
+                 const T *from, gdb_byte *to) const;
+};
+
+
+/* Convert TO/FROM target to the host floating-point format T.
+
+   If the host and target formats agree, we just copy the raw data
+   into the appropriate type of variable and return, letting the host
+   increase precision as necessary.  Otherwise, we call the conversion
+   routine and let it do the dirty work.  Note that even if the target
+   and host floating-point formats match, the length of the types
+   might still be different, so the conversion routines must make sure
+   to not overrun any buffers.  For example, on x86, long double is
+   the 80-bit extended precision type on both 32-bit and 64-bit ABIs,
+   but by default it is stored as 12 bytes on 32-bit, and 16 bytes on
+   64-bit, for alignment reasons.  See comment in store_typed_floating
+   for a discussion about zeroing out remaining bytes in the target
+   buffer.  */
+
+static const struct floatformat *host_float_format = GDB_HOST_FLOAT_FORMAT;
+static const struct floatformat *host_double_format = GDB_HOST_DOUBLE_FORMAT;
+static const struct floatformat *host_long_double_format
+  = GDB_HOST_LONG_DOUBLE_FORMAT;
+
+/* Convert target floating-point value at FROM in format FMT to host
+   floating-point format of type T.  */
+template<typename T> void
+host_float_ops<T>::from_target (const struct floatformat *fmt,
+                               const gdb_byte *from, T *to) const
+{
+  gdb_assert (fmt != NULL);
+
+  if (fmt == host_float_format)
+    {
+      float val = 0;
+
+      memcpy (&val, from, floatformat_totalsize_bytes (fmt));
+      *to = val;
+      return;
+    }
+  else if (fmt == host_double_format)
+    {
+      double val = 0;
+
+      memcpy (&val, from, floatformat_totalsize_bytes (fmt));
+      *to = val;
+      return;
+    }
+  else if (fmt == host_long_double_format)
+    {
+      long double val = 0;
+
+      memcpy (&val, from, floatformat_totalsize_bytes (fmt));
+      *to = val;
+      return;
+    }
+
+  unsigned char *ufrom = (unsigned char *) from;
+  long exponent;
+  unsigned long mant;
+  unsigned int mant_bits, mant_off;
+  int mant_bits_left;
+  int special_exponent;                /* It's a NaN, denorm or zero.  */
+  enum floatformat_byteorders order;
+  unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
+  enum float_kind kind;
+
+  gdb_assert (fmt->totalsize
+             <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
+
+  /* For non-numbers, reuse libiberty's logic to find the correct
+     format.  We do not lose any precision in this case by passing
+     through a double.  */
+  kind = floatformat_classify (fmt, (const bfd_byte *) from);
+  if (kind == float_infinite || kind == float_nan)
+    {
+      double dto;
+
+      floatformat_to_double    /* ARI: floatformat_to_double */
+       (fmt->split_half ? fmt->split_half : fmt, from, &dto);
+      *to = (T) dto;
+      return;
+    }
+
+  order = floatformat_normalize_byteorder (fmt, ufrom, newfrom);
+
+  if (order != fmt->byteorder)
+    ufrom = newfrom;
+
+  if (fmt->split_half)
+    {
+      T dtop, dbot;
+
+      from_target (fmt->split_half, ufrom, &dtop);
+      /* Preserve the sign of 0, which is the sign of the top
+        half.  */
+      if (dtop == 0.0)
+       {
+         *to = dtop;
+         return;
+       }
+      from_target (fmt->split_half,
+                  ufrom + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, &dbot);
+      *to = dtop + dbot;
+      return;
+    }
+
+  exponent = get_field (ufrom, order, fmt->totalsize, fmt->exp_start,
+                       fmt->exp_len);
+  /* Note that if exponent indicates a NaN, we can't really do anything useful
+     (not knowing if the host has NaN's, or how to build one).  So it will
+     end up as an infinity or something close; that is OK.  */
+
+  mant_bits_left = fmt->man_len;
+  mant_off = fmt->man_start;
+  T dto = 0.0;
+
+  special_exponent = exponent == 0 || exponent == fmt->exp_nan;
+
+  /* Don't bias NaNs.  Use minimum exponent for denorms.  For
+     simplicity, we don't check for zero as the exponent doesn't matter.
+     Note the cast to int; exp_bias is unsigned, so it's important to
+     make sure the operation is done in signed arithmetic.  */
+  if (!special_exponent)
+    exponent -= fmt->exp_bias;
+  else if (exponent == 0)
+    exponent = 1 - fmt->exp_bias;
+
+  /* Build the result algebraically.  Might go infinite, underflow, etc;
+     who cares.  */
+
+  /* If this format uses a hidden bit, explicitly add it in now.  Otherwise,
+     increment the exponent by one to account for the integer bit.  */
+
+  if (!special_exponent)
+    {
+      if (fmt->intbit == floatformat_intbit_no)
+       dto = ldexp (1.0, exponent);
+      else
+       exponent++;
+    }
+
+  while (mant_bits_left > 0)
+    {
+      mant_bits = std::min (mant_bits_left, 32);
+
+      mant = get_field (ufrom, order, fmt->totalsize, mant_off, mant_bits);
+
+      dto += ldexp ((T) mant, exponent - mant_bits);
+      exponent -= mant_bits;
+      mant_off += mant_bits;
+      mant_bits_left -= mant_bits;
+    }
+
+  /* Negate it if negative.  */
+  if (get_field (ufrom, order, fmt->totalsize, fmt->sign_start, 1))
+    dto = -dto;
+  *to = dto;
+}
+
+template<typename T> void
+host_float_ops<T>::from_target (const struct type *type,
+                               const gdb_byte *from, T *to) const
+{
+  from_target (floatformat_from_type (type), from, to);
+}
+
+/* Convert host floating-point value of type T to target floating-point
+   value in format FMT and store at TO.  */
+template<typename T> void
+host_float_ops<T>::to_target (const struct floatformat *fmt,
+                             const T *from, gdb_byte *to) const
+{
+  gdb_assert (fmt != NULL);
+
+  if (fmt == host_float_format)
+    {
+      float val = *from;
+
+      memcpy (to, &val, floatformat_totalsize_bytes (fmt));
+      return;
+    }
+  else if (fmt == host_double_format)
+    {
+      double val = *from;
+
+      memcpy (to, &val, floatformat_totalsize_bytes (fmt));
+      return;
+    }
+  else if (fmt == host_long_double_format)
+    {
+      long double val = *from;
+
+      memcpy (to, &val, floatformat_totalsize_bytes (fmt));
+      return;
+    }
+
+  T dfrom;
+  int exponent;
+  T mant;
+  unsigned int mant_bits, mant_off;
+  int mant_bits_left;
+  unsigned char *uto = (unsigned char *) to;
+  enum floatformat_byteorders order = fmt->byteorder;
+  unsigned char newto[FLOATFORMAT_LARGEST_BYTES];
+
+  if (order != floatformat_little)
+    order = floatformat_big;
+
+  if (order != fmt->byteorder)
+    uto = newto;
+
+  memcpy (&dfrom, from, sizeof (dfrom));
+  memset (uto, 0, floatformat_totalsize_bytes (fmt));
+
+  if (fmt->split_half)
+    {
+      /* Use static volatile to ensure that any excess precision is
+        removed via storing in memory, and so the top half really is
+        the result of converting to double.  */
+      static volatile double dtop, dbot;
+      T dtopnv, dbotnv;
+
+      dtop = (double) dfrom;
+      /* If the rounded top half is Inf, the bottom must be 0 not NaN
+        or Inf.  */
+      if (dtop + dtop == dtop && dtop != 0.0)
+       dbot = 0.0;
+      else
+       dbot = (double) (dfrom - (T) dtop);
+      dtopnv = dtop;
+      dbotnv = dbot;
+      to_target (fmt->split_half, &dtopnv, uto);
+      to_target (fmt->split_half, &dbotnv,
+                uto + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2);
+      return;
+    }
+
+  if (dfrom == 0)
+    goto finalize_byteorder;   /* Result is zero */
+  if (dfrom != dfrom)          /* Result is NaN */
+    {
+      /* From is NaN */
+      put_field (uto, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, fmt->exp_nan);
+      /* Be sure it's not infinity, but NaN value is irrel.  */
+      put_field (uto, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 1);
+      goto finalize_byteorder;
+    }
+
+  /* If negative, set the sign bit.  */
+  if (dfrom < 0)
+    {
+      put_field (uto, order, fmt->totalsize, fmt->sign_start, 1, 1);
+      dfrom = -dfrom;
+    }
+
+  if (dfrom + dfrom == dfrom && dfrom != 0.0)  /* Result is Infinity.  */
+    {
+      /* Infinity exponent is same as NaN's.  */
+      put_field (uto, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, fmt->exp_nan);
+      /* Infinity mantissa is all zeroes.  */
+      put_field (uto, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 0);
+      goto finalize_byteorder;
+    }
+
+  mant = frexp (dfrom, &exponent);
+
+  if (exponent + fmt->exp_bias <= 0)
+    {
+      /* The value is too small to be expressed in the destination
+        type (not enough bits in the exponent.  Treat as 0.  */
+      put_field (uto, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, 0);
+      put_field (uto, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 0);
+      goto finalize_byteorder;
+    }
+
+  if (exponent + fmt->exp_bias >= (1 << fmt->exp_len))
+    {
+      /* The value is too large to fit into the destination.
+        Treat as infinity.  */
+      put_field (uto, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, fmt->exp_nan);
+      put_field (uto, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 0);
+      goto finalize_byteorder;
+    }
+
+  put_field (uto, order, fmt->totalsize, fmt->exp_start, fmt->exp_len,
+            exponent + fmt->exp_bias - 1);
+
+  mant_bits_left = fmt->man_len;
+  mant_off = fmt->man_start;
+  while (mant_bits_left > 0)
+    {
+      unsigned long mant_long;
+
+      mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
+
+      mant *= 4294967296.0;
+      mant_long = ((unsigned long) mant) & 0xffffffffL;
+      mant -= mant_long;
+
+      /* If the integer bit is implicit, then we need to discard it.
+        If we are discarding a zero, we should be (but are not) creating
+        a denormalized number which means adjusting the exponent
+        (I think).  */
+      if (mant_bits_left == fmt->man_len
+         && fmt->intbit == floatformat_intbit_no)
+       {
+         mant_long <<= 1;
+         mant_long &= 0xffffffffL;
+         /* If we are processing the top 32 mantissa bits of a doublest
+            so as to convert to a float value with implied integer bit,
+            we will only be putting 31 of those 32 bits into the
+            final value due to the discarding of the top bit.  In the
+            case of a small float value where the number of mantissa
+            bits is less than 32, discarding the top bit does not alter
+            the number of bits we will be adding to the result.  */
+         if (mant_bits == 32)
+           mant_bits -= 1;
+       }
+
+      if (mant_bits < 32)
+       {
+         /* The bits we want are in the most significant MANT_BITS bits of
+            mant_long.  Move them to the least significant.  */
+         mant_long >>= 32 - mant_bits;
+       }
+
+      put_field (uto, order, fmt->totalsize,
+                mant_off, mant_bits, mant_long);
+      mant_off += mant_bits;
+      mant_bits_left -= mant_bits;
+    }
+
+ finalize_byteorder:
+  /* Do we need to byte-swap the words in the result?  */
+  if (order != fmt->byteorder)
+    floatformat_normalize_byteorder (fmt, newto, to);
+}
+
+template<typename T> void
+host_float_ops<T>::to_target (const struct type *type,
+                             const T *from, gdb_byte *to) const
+{
+  /* Ensure possible padding bytes in the target buffer are zeroed out.  */
+  memset (to, 0, TYPE_LENGTH (type));
+
+  to_target (floatformat_from_type (type), from, to);
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to a string, optionally using the print format FORMAT.  */
+template<typename T> struct printf_length_modifier
+{
+  static constexpr char value = 0;
+};
+template<> struct printf_length_modifier<long double>
+{
+  static constexpr char value = 'L';
+};
+template<typename T> std::string
+host_float_ops<T>::to_string (const gdb_byte *addr, const struct type *type,
+                             const char *format) const
+{
+  /* Determine the format string to use on the host side.  */
+  constexpr char length = printf_length_modifier<T>::value;
+  const struct floatformat *fmt = floatformat_from_type (type);
+  std::string host_format = floatformat_printf_format (fmt, format, length);
+
+  T host_float;
+  from_target (type, addr, &host_float);
+
+  DIAGNOSTIC_PUSH
+  DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+  return string_printf (host_format.c_str (), host_float);
+  DIAGNOSTIC_POP
+}
+
+/* Parse string IN into a target floating-number of type TYPE and
+   store it as byte-stream ADDR.  Return whether parsing succeeded.  */
+template<typename T> struct scanf_length_modifier
+{
+  static constexpr char value = 0;
+};
+template<> struct scanf_length_modifier<double>
+{
+  static constexpr char value = 'l';
+};
+template<> struct scanf_length_modifier<long double>
+{
+  static constexpr char value = 'L';
+};
+template<typename T> bool
+host_float_ops<T>::from_string (gdb_byte *addr, const struct type *type,
+                               const std::string &in) const
+{
+  T host_float;
+  int n, num;
+
+  std::string scan_format = "%";
+  if (scanf_length_modifier<T>::value)
+    scan_format += scanf_length_modifier<T>::value;
+  scan_format += "g%n";
+
+  DIAGNOSTIC_PUSH
+  DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+  num = sscanf (in.c_str (), scan_format.c_str(), &host_float, &n);
+  DIAGNOSTIC_POP
+
+  /* The sscanf man page suggests not making any assumptions on the effect
+     of %n on the result, so we don't.
+     That is why we simply test num == 0.  */
+  if (num == 0)
+    return false;
+
+  /* We only accept the whole string.  */
+  if (in[n])
+    return false;
+
+  to_target (type, &host_float, addr);
+  return true;
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to an integer value (rounding towards zero).  */
+template<typename T> LONGEST
+host_float_ops<T>::to_longest (const gdb_byte *addr,
+                              const struct type *type) const
+{
+  T host_float;
+  from_target (type, addr, &host_float);
+  T min_possible_range = static_cast<T>(std::numeric_limits<LONGEST>::min());
+  T max_possible_range = -min_possible_range;
+  /* host_float can be converted to an integer as long as it's in
+     the range [min_possible_range, max_possible_range). If not, it is either
+     too large, or too small, or is NaN; in this case return the maximum or
+     minimum possible value.  */
+  if (host_float < max_possible_range && host_float >= min_possible_range)
+    return static_cast<LONGEST> (host_float);
+  if (host_float < min_possible_range)
+    return std::numeric_limits<LONGEST>::min();
+  /* This line will be executed if host_float is NaN.  */
+  return std::numeric_limits<LONGEST>::max();
+}
+
+/* Convert signed integer VAL to a target floating-number of type TYPE
+   and store it as byte-stream ADDR.  */
+template<typename T> void
+host_float_ops<T>::from_longest (gdb_byte *addr, const struct type *type,
+                                LONGEST val) const
+{
+  T host_float = (T) val;
+  to_target (type, &host_float, addr);
+}
+
+/* Convert unsigned integer VAL to a target floating-number of type TYPE
+   and store it as byte-stream ADDR.  */
+template<typename T> void
+host_float_ops<T>::from_ulongest (gdb_byte *addr, const struct type *type,
+                                 ULONGEST val) const
+{
+  T host_float = (T) val;
+  to_target (type, &host_float, addr);
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to a floating-point value in the host "double" format.  */
+template<typename T> double
+host_float_ops<T>::to_host_double (const gdb_byte *addr,
+                                  const struct type *type) const
+{
+  T host_float;
+  from_target (type, addr, &host_float);
+  return (double) host_float;
+}
+
+/* Convert floating-point value VAL in the host "double" format to a target
+   floating-number of type TYPE and store it as byte-stream ADDR.  */
+template<typename T> void
+host_float_ops<T>::from_host_double (gdb_byte *addr, const struct type *type,
+                                    double val) const
+{
+  T host_float = (T) val;
+  to_target (type, &host_float, addr);
+}
+
+/* Convert a floating-point number of type FROM_TYPE from the target
+   byte-stream FROM to a floating-point number of type TO_TYPE, and
+   store it to the target byte-stream TO.  */
+template<typename T> void
+host_float_ops<T>::convert (const gdb_byte *from,
+                           const struct type *from_type,
+                           gdb_byte *to,
+                           const struct type *to_type) const
+{
+  T host_float;
+  from_target (from_type, from, &host_float);
+  to_target (to_type, &host_float, to);
+}
+
+/* Perform the binary operation indicated by OPCODE, using as operands the
+   target byte streams X and Y, interpreted as floating-point numbers of
+   types TYPE_X and TYPE_Y, respectively.  Convert the result to format
+   TYPE_RES and store it into the byte-stream RES.  */
+template<typename T> void
+host_float_ops<T>::binop (enum exp_opcode op,
+                         const gdb_byte *x, const struct type *type_x,
+                         const gdb_byte *y, const struct type *type_y,
+                         gdb_byte *res, const struct type *type_res) const
+{
+  T v1, v2, v = 0;
+
+  from_target (type_x, x, &v1);
+  from_target (type_y, y, &v2);
+
+  switch (op)
+    {
+      case BINOP_ADD:
+       v = v1 + v2;
+       break;
+
+      case BINOP_SUB:
+       v = v1 - v2;
+       break;
+
+      case BINOP_MUL:
+       v = v1 * v2;
+       break;
+
+      case BINOP_DIV:
+       v = v1 / v2;
+       break;
+
+      case BINOP_EXP:
+       errno = 0;
+       v = pow (v1, v2);
+       if (errno)
+         error (_("Cannot perform exponentiation: %s"),
+                safe_strerror (errno));
+       break;
+
+      case BINOP_MIN:
+       v = v1 < v2 ? v1 : v2;
+       break;
+
+      case BINOP_MAX:
+       v = v1 > v2 ? v1 : v2;
+       break;
+
+      default:
+       error (_("Integer-only operation on floating point number."));
+       break;
+    }
+
+  to_target (type_res, &v, res);
+}
+
+/* Compare the two target byte streams X and Y, interpreted as floating-point
+   numbers of types TYPE_X and TYPE_Y, respectively.  Return zero if X and Y
+   are equal, -1 if X is less than Y, and 1 otherwise.  */
+template<typename T> int
+host_float_ops<T>::compare (const gdb_byte *x, const struct type *type_x,
+                           const gdb_byte *y, const struct type *type_y) const
+{
+  T v1, v2;
+
+  from_target (type_x, x, &v1);
+  from_target (type_y, y, &v2);
+
+  if (v1 == v2)
+    return 0;
+  if (v1 < v2)
+    return -1;
+  return 1;
+}
+
+
+/* Implementation of target_float_ops using the MPFR library
+   mpfr_t as intermediate type.  */
+
+#ifdef HAVE_LIBMPFR
+
+#define MPFR_USE_INTMAX_T
+
+#include <mpfr.h>
+
+class mpfr_float_ops : public target_float_ops
+{
+public:
+  std::string to_string (const gdb_byte *addr, const struct type *type,
+                        const char *format) const override;
+  bool from_string (gdb_byte *addr, const struct type *type,
+                   const std::string &string) const override;
+
+  LONGEST to_longest (const gdb_byte *addr,
+                     const struct type *type) const override;
+  void from_longest (gdb_byte *addr, const struct type *type,
+                    LONGEST val) const override;
+  void from_ulongest (gdb_byte *addr, const struct type *type,
+                     ULONGEST val) const override;
+  double to_host_double (const gdb_byte *addr,
+                        const struct type *type) const override;
+  void from_host_double (gdb_byte *addr, const struct type *type,
+                        double val) const override;
+  void convert (const gdb_byte *from, const struct type *from_type,
+               gdb_byte *to, const struct type *to_type) const override;
+
+  void binop (enum exp_opcode opcode,
+             const gdb_byte *x, const struct type *type_x,
+             const gdb_byte *y, const struct type *type_y,
+             gdb_byte *res, const struct type *type_res) const override;
+  int compare (const gdb_byte *x, const struct type *type_x,
+              const gdb_byte *y, const struct type *type_y) const override;
+
+private:
+  /* Local wrapper class to handle mpfr_t initalization and cleanup.  */
+  class gdb_mpfr
+  {
+  public:
+    mpfr_t val;
+
+    gdb_mpfr (const struct type *type)
+    {
+      const struct floatformat *fmt = floatformat_from_type (type);
+      mpfr_init2 (val, floatformat_precision (fmt));
+    }
+
+    gdb_mpfr (const gdb_mpfr &source)
+    {
+      mpfr_init2 (val, mpfr_get_prec (source.val));
+    }
+
+    ~gdb_mpfr ()
+    {
+      mpfr_clear (val);
+    }
+  };
+
+  void from_target (const struct floatformat *fmt,
+               const gdb_byte *from, gdb_mpfr &to) const;
+  void from_target (const struct type *type,
+               const gdb_byte *from, gdb_mpfr &to) const;
+
+  void to_target (const struct type *type,
+                 const gdb_mpfr &from, gdb_byte *to) const;
+  void to_target (const struct floatformat *fmt,
+                 const gdb_mpfr &from, gdb_byte *to) const;
+};
+
+
+/* Convert TO/FROM target floating-point format to mpfr_t.  */
+
+void
+mpfr_float_ops::from_target (const struct floatformat *fmt,
+                            const gdb_byte *orig_from, gdb_mpfr &to) const
+{
+  const gdb_byte *from = orig_from;
+  mpfr_exp_t exponent;
+  unsigned long mant;
+  unsigned int mant_bits, mant_off;
+  int mant_bits_left;
+  int special_exponent;                /* It's a NaN, denorm or zero.  */
+  enum floatformat_byteorders order;
+  unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
+  enum float_kind kind;
+
+  gdb_assert (fmt->totalsize
+             <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
+
+  /* Handle non-numbers.  */
+  kind = floatformat_classify (fmt, from);
+  if (kind == float_infinite)
+    {
+      mpfr_set_inf (to.val, floatformat_is_negative (fmt, from) ? -1 : 1);
+      return;
+    }
+  if (kind == float_nan)
+    {
+      mpfr_set_nan (to.val);
+      return;
+    }
+
+  order = floatformat_normalize_byteorder (fmt, from, newfrom);
+
+  if (order != fmt->byteorder)
+    from = newfrom;
+
+  if (fmt->split_half)
+    {
+      gdb_mpfr top (to), bot (to);
+
+      from_target (fmt->split_half, from, top);
+      /* Preserve the sign of 0, which is the sign of the top half.  */
+      if (mpfr_zero_p (top.val))
+       {
+         mpfr_set (to.val, top.val, MPFR_RNDN);
+         return;
+       }
+      from_target (fmt->split_half,
+              from + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, bot);
+      mpfr_add (to.val, top.val, bot.val, MPFR_RNDN);
+      return;
+    }
+
+  exponent = get_field (from, order, fmt->totalsize, fmt->exp_start,
+                       fmt->exp_len);
+  /* Note that if exponent indicates a NaN, we can't really do anything useful
+     (not knowing if the host has NaN's, or how to build one).  So it will
+     end up as an infinity or something close; that is OK.  */
+
+  mant_bits_left = fmt->man_len;
+  mant_off = fmt->man_start;
+  mpfr_set_zero (to.val, 0);
+
+  special_exponent = exponent == 0 || exponent == fmt->exp_nan;
+
+  /* Don't bias NaNs.  Use minimum exponent for denorms.  For
+     simplicity, we don't check for zero as the exponent doesn't matter.
+     Note the cast to int; exp_bias is unsigned, so it's important to
+     make sure the operation is done in signed arithmetic.  */
+  if (!special_exponent)
+    exponent -= fmt->exp_bias;
+  else if (exponent == 0)
+    exponent = 1 - fmt->exp_bias;
+
+  /* Build the result algebraically.  Might go infinite, underflow, etc;
+     who cares.  */
+
+  /* If this format uses a hidden bit, explicitly add it in now.  Otherwise,
+     increment the exponent by one to account for the integer bit.  */
+
+  if (!special_exponent)
+    {
+      if (fmt->intbit == floatformat_intbit_no)
+       mpfr_set_ui_2exp (to.val, 1, exponent, MPFR_RNDN);
+      else
+       exponent++;
+    }
+
+  gdb_mpfr tmp (to);
+
+  while (mant_bits_left > 0)
+    {
+      mant_bits = std::min (mant_bits_left, 32);
+
+      mant = get_field (from, order, fmt->totalsize, mant_off, mant_bits);
+
+      mpfr_set_ui (tmp.val, mant, MPFR_RNDN);
+      mpfr_mul_2si (tmp.val, tmp.val, exponent - mant_bits, MPFR_RNDN);
+      mpfr_add (to.val, to.val, tmp.val, MPFR_RNDN);
+      exponent -= mant_bits;
+      mant_off += mant_bits;
+      mant_bits_left -= mant_bits;
+    }
+
+  /* Negate it if negative.  */
+  if (get_field (from, order, fmt->totalsize, fmt->sign_start, 1))
+    mpfr_neg (to.val, to.val, MPFR_RNDN);
+}
+
+void
+mpfr_float_ops::from_target (const struct type *type,
+                            const gdb_byte *from, gdb_mpfr &to) const
+{
+  from_target (floatformat_from_type (type), from, to);
+}
+
+void
+mpfr_float_ops::to_target (const struct floatformat *fmt,
+                          const gdb_mpfr &from, gdb_byte *orig_to) const
+{
+  unsigned char *to = orig_to;
+  mpfr_exp_t exponent;
+  unsigned int mant_bits, mant_off;
+  int mant_bits_left;
+  enum floatformat_byteorders order = fmt->byteorder;
+  unsigned char newto[FLOATFORMAT_LARGEST_BYTES];
+
+  if (order != floatformat_little)
+    order = floatformat_big;
+
+  if (order != fmt->byteorder)
+    to = newto;
+
+  memset (to, 0, floatformat_totalsize_bytes (fmt));
+
+  if (fmt->split_half)
+    {
+      gdb_mpfr top (from), bot (from);
+
+      mpfr_set (top.val, from.val, MPFR_RNDN);
+      /* If the rounded top half is Inf, the bottom must be 0 not NaN
+        or Inf.  */
+      if (mpfr_inf_p (top.val))
+       mpfr_set_zero (bot.val, 0);
+      else
+       mpfr_sub (bot.val, from.val, top.val, MPFR_RNDN);
+
+      to_target (fmt->split_half, top, to);
+      to_target (fmt->split_half, bot,
+                to + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2);
+      return;
+    }
+
+  gdb_mpfr tmp (from);
+
+  if (mpfr_zero_p (from.val))
+    goto finalize_byteorder;   /* Result is zero */
+
+  mpfr_set (tmp.val, from.val, MPFR_RNDN);
+
+  if (mpfr_nan_p (tmp.val))    /* Result is NaN */
+    {
+      /* From is NaN */
+      put_field (to, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, fmt->exp_nan);
+      /* Be sure it's not infinity, but NaN value is irrel.  */
+      put_field (to, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 1);
+      goto finalize_byteorder;
+    }
+
+  /* If negative, set the sign bit.  */
+  if (mpfr_sgn (tmp.val) < 0)
+    {
+      put_field (to, order, fmt->totalsize, fmt->sign_start, 1, 1);
+      mpfr_neg (tmp.val, tmp.val, MPFR_RNDN);
+    }
+
+  if (mpfr_inf_p (tmp.val))            /* Result is Infinity.  */
+    {
+      /* Infinity exponent is same as NaN's.  */
+      put_field (to, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, fmt->exp_nan);
+      /* Infinity mantissa is all zeroes.  */
+      put_field (to, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 0);
+      goto finalize_byteorder;
+    }
+
+  mpfr_frexp (&exponent, tmp.val, tmp.val, MPFR_RNDN);
+
+  if (exponent + fmt->exp_bias <= 0)
+    {
+      /* The value is too small to be expressed in the destination
+        type (not enough bits in the exponent.  Treat as 0.  */
+      put_field (to, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, 0);
+      put_field (to, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 0);
+      goto finalize_byteorder;
+    }
+
+  if (exponent + fmt->exp_bias >= (1 << fmt->exp_len))
+    {
+      /* The value is too large to fit into the destination.
+        Treat as infinity.  */
+      put_field (to, order, fmt->totalsize, fmt->exp_start,
+                fmt->exp_len, fmt->exp_nan);
+      put_field (to, order, fmt->totalsize, fmt->man_start,
+                fmt->man_len, 0);
+      goto finalize_byteorder;
+    }
+
+  put_field (to, order, fmt->totalsize, fmt->exp_start, fmt->exp_len,
+            exponent + fmt->exp_bias - 1);
+
+  mant_bits_left = fmt->man_len;
+  mant_off = fmt->man_start;
+  while (mant_bits_left > 0)
+    {
+      unsigned long mant_long;
+
+      mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
+
+      mpfr_mul_2ui (tmp.val, tmp.val, 32, MPFR_RNDN);
+      mant_long = mpfr_get_ui (tmp.val, MPFR_RNDZ) & 0xffffffffL;
+      mpfr_sub_ui (tmp.val, tmp.val, mant_long, MPFR_RNDZ);
+
+      /* If the integer bit is implicit, then we need to discard it.
+        If we are discarding a zero, we should be (but are not) creating
+        a denormalized number which means adjusting the exponent
+        (I think).  */
+      if (mant_bits_left == fmt->man_len
+         && fmt->intbit == floatformat_intbit_no)
+       {
+         mant_long <<= 1;
+         mant_long &= 0xffffffffL;
+         /* If we are processing the top 32 mantissa bits of a doublest
+            so as to convert to a float value with implied integer bit,
+            we will only be putting 31 of those 32 bits into the
+            final value due to the discarding of the top bit.  In the
+            case of a small float value where the number of mantissa
+            bits is less than 32, discarding the top bit does not alter
+            the number of bits we will be adding to the result.  */
+         if (mant_bits == 32)
+           mant_bits -= 1;
+       }
+
+      if (mant_bits < 32)
+       {
+         /* The bits we want are in the most significant MANT_BITS bits of
+            mant_long.  Move them to the least significant.  */
+         mant_long >>= 32 - mant_bits;
+       }
+
+      put_field (to, order, fmt->totalsize,
+                mant_off, mant_bits, mant_long);
+      mant_off += mant_bits;
+      mant_bits_left -= mant_bits;
+    }
+
+ finalize_byteorder:
+  /* Do we need to byte-swap the words in the result?  */
+  if (order != fmt->byteorder)
+    floatformat_normalize_byteorder (fmt, newto, orig_to);
+}
+
+void
+mpfr_float_ops::to_target (const struct type *type,
+                          const gdb_mpfr &from, gdb_byte *to) const
+{
+  /* Ensure possible padding bytes in the target buffer are zeroed out.  */
+  memset (to, 0, TYPE_LENGTH (type));
+
+  to_target (floatformat_from_type (type), from, to);
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to a string, optionally using the print format FORMAT.  */
+std::string
+mpfr_float_ops::to_string (const gdb_byte *addr,
+                          const struct type *type,
+                          const char *format) const
+{
+  const struct floatformat *fmt = floatformat_from_type (type);
+
+  /* Unless we need to adhere to a specific format, provide special
+     output for certain cases.  */
+  if (format == nullptr)
+    {
+      /* Detect invalid representations.  */
+      if (!floatformat_is_valid (fmt, addr))
+       return "<invalid float value>";
+
+      /* Handle NaN and Inf.  */
+      enum float_kind kind = floatformat_classify (fmt, addr);
+      if (kind == float_nan)
+       {
+         const char *sign = floatformat_is_negative (fmt, addr)? "-" : "";
+         const char *mantissa = floatformat_mantissa (fmt, addr);
+         return string_printf ("%snan(0x%s)", sign, mantissa);
+       }
+      else if (kind == float_infinite)
+       {
+         const char *sign = floatformat_is_negative (fmt, addr)? "-" : "";
+         return string_printf ("%sinf", sign);
+       }
+    }
+
+  /* Determine the format string to use on the host side.  */
+  std::string host_format = floatformat_printf_format (fmt, format, 'R');
+
+  gdb_mpfr tmp (type);
+  from_target (type, addr, tmp);
+
+  int size = mpfr_snprintf (NULL, 0, host_format.c_str (), tmp.val);
+  std::string str (size, '\0');
+  mpfr_sprintf (&str[0], host_format.c_str (), tmp.val);
+
+  return str;
+}
+
+/* Parse string STRING into a target floating-number of type TYPE and
+   store it as byte-stream ADDR.  Return whether parsing succeeded.  */
+bool
+mpfr_float_ops::from_string (gdb_byte *addr,
+                            const struct type *type,
+                            const std::string &in) const
+{
+  gdb_mpfr tmp (type);
+
+  char *endptr;
+  mpfr_strtofr (tmp.val, in.c_str (), &endptr, 0, MPFR_RNDN);
+
+  /* We only accept the whole string.  */
+  if (*endptr)
+    return false;
+
+  to_target (type, tmp, addr);
+  return true;
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to an integer value (rounding towards zero).  */
+LONGEST
+mpfr_float_ops::to_longest (const gdb_byte *addr,
+                           const struct type *type) const
+{
+  gdb_mpfr tmp (type);
+  from_target (type, addr, tmp);
+  return mpfr_get_sj (tmp.val, MPFR_RNDZ);
+}
+
+/* Convert signed integer VAL to a target floating-number of type TYPE
+   and store it as byte-stream ADDR.  */
+void
+mpfr_float_ops::from_longest (gdb_byte *addr,
+                             const struct type *type,
+                             LONGEST val) const
+{
+  gdb_mpfr tmp (type);
+  mpfr_set_sj (tmp.val, val, MPFR_RNDN);
+  to_target (type, tmp, addr);
+}
+
+/* Convert unsigned integer VAL to a target floating-number of type TYPE
+   and store it as byte-stream ADDR.  */
+void
+mpfr_float_ops::from_ulongest (gdb_byte *addr,
+                              const struct type *type,
+                              ULONGEST val) const
+{
+  gdb_mpfr tmp (type);
+  mpfr_set_uj (tmp.val, val, MPFR_RNDN);
+  to_target (type, tmp, addr);
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to a floating-point value in the host "double" format.  */
+double
+mpfr_float_ops::to_host_double (const gdb_byte *addr,
+                               const struct type *type) const
+{
+  gdb_mpfr tmp (type);
+  from_target (type, addr, tmp);
+  return mpfr_get_d (tmp.val, MPFR_RNDN);
+}
+
+/* Convert floating-point value VAL in the host "double" format to a target
+   floating-number of type TYPE and store it as byte-stream ADDR.  */
+void
+mpfr_float_ops::from_host_double (gdb_byte *addr,
+                                 const struct type *type,
+                                 double val) const
+{
+  gdb_mpfr tmp (type);
+  mpfr_set_d (tmp.val, val, MPFR_RNDN);
+  to_target (type, tmp, addr);
+}
+
+/* Convert a floating-point number of type FROM_TYPE from the target
+   byte-stream FROM to a floating-point number of type TO_TYPE, and
+   store it to the target byte-stream TO.  */
+void
+mpfr_float_ops::convert (const gdb_byte *from,
+                        const struct type *from_type,
+                        gdb_byte *to,
+                        const struct type *to_type) const
+{
+  gdb_mpfr from_tmp (from_type), to_tmp (to_type);
+  from_target (from_type, from, from_tmp);
+  mpfr_set (to_tmp.val, from_tmp.val, MPFR_RNDN);
+  to_target (to_type, to_tmp, to);
+}
+
+/* Perform the binary operation indicated by OPCODE, using as operands the
+   target byte streams X and Y, interpreted as floating-point numbers of
+   types TYPE_X and TYPE_Y, respectively.  Convert the result to type
+   TYPE_RES and store it into the byte-stream RES.  */
+void
+mpfr_float_ops::binop (enum exp_opcode op,
+                      const gdb_byte *x, const struct type *type_x,
+                      const gdb_byte *y, const struct type *type_y,
+                      gdb_byte *res, const struct type *type_res) const
+{
+  gdb_mpfr x_tmp (type_x), y_tmp (type_y), tmp (type_res);
+
+  from_target (type_x, x, x_tmp);
+  from_target (type_y, y, y_tmp);
+
+  switch (op)
+    {
+      case BINOP_ADD:
+       mpfr_add (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      case BINOP_SUB:
+       mpfr_sub (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      case BINOP_MUL:
+       mpfr_mul (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      case BINOP_DIV:
+       mpfr_div (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      case BINOP_EXP:
+       mpfr_pow (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      case BINOP_MIN:
+       mpfr_min (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      case BINOP_MAX:
+       mpfr_max (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN);
+       break;
+
+      default:
+       error (_("Integer-only operation on floating point number."));
+       break;
+    }
+
+  to_target (type_res, tmp, res);
+}
+
+/* Compare the two target byte streams X and Y, interpreted as floating-point
+   numbers of types TYPE_X and TYPE_Y, respectively.  Return zero if X and Y
+   are equal, -1 if X is less than Y, and 1 otherwise.  */
+int
+mpfr_float_ops::compare (const gdb_byte *x, const struct type *type_x,
+                        const gdb_byte *y, const struct type *type_y) const
+{
+  gdb_mpfr x_tmp (type_x), y_tmp (type_y);
+
+  from_target (type_x, x, x_tmp);
+  from_target (type_y, y, y_tmp);
+
+  if (mpfr_equal_p (x_tmp.val, y_tmp.val))
+    return 0;
+  else if (mpfr_less_p (x_tmp.val, y_tmp.val))
+    return -1;
+  else
+    return 1;
+}
+
+#endif
+
+
+/* Helper routines operating on decimal floating-point data.  */
+
+/* Decimal floating point is one of the extension to IEEE 754, which is
+   described in http://grouper.ieee.org/groups/754/revision.html and
+   http://www2.hursley.ibm.com/decimal/.  It completes binary floating
+   point by representing floating point more exactly.  */
+
+/* The order of the following headers is important for making sure
+   decNumber structure is large enough to hold decimal128 digits.  */
+
+#include "dpd/decimal128.h"
+#include "dpd/decimal64.h"
+#include "dpd/decimal32.h"
+
+/* When using decimal128, this is the maximum string length + 1
+   (value comes from libdecnumber's DECIMAL128_String constant).  */
+#define MAX_DECIMAL_STRING  43
+
+/* In GDB, we are using an array of gdb_byte to represent decimal values.
+   They are stored in host byte order.  This routine does the conversion if
+   the target byte order is different.  */
+static void
+match_endianness (const gdb_byte *from, const struct type *type, gdb_byte *to)
+{
+  gdb_assert (type->code () == TYPE_CODE_DECFLOAT);
+
+  int len = TYPE_LENGTH (type);
+  int i;
+
+#if WORDS_BIGENDIAN
+#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_LITTLE
+#else
+#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG
+#endif
+
+  if (type_byte_order (type) == OPPOSITE_BYTE_ORDER)
+    for (i = 0; i < len; i++)
+      to[i] = from[len - i - 1];
+  else
+    for (i = 0; i < len; i++)
+      to[i] = from[i];
+
+  return;
+}
+
+/* Helper function to get the appropriate libdecnumber context for each size
+   of decimal float.  */
+static void
+set_decnumber_context (decContext *ctx, const struct type *type)
+{
+  gdb_assert (type->code () == TYPE_CODE_DECFLOAT);
+
+  switch (TYPE_LENGTH (type))
+    {
+      case 4:
+       decContextDefault (ctx, DEC_INIT_DECIMAL32);
+       break;
+      case 8:
+       decContextDefault (ctx, DEC_INIT_DECIMAL64);
+       break;
+      case 16:
+       decContextDefault (ctx, DEC_INIT_DECIMAL128);
+       break;
+    }
+
+  ctx->traps = 0;
+}
+
+/* Check for errors signaled in the decimal context structure.  */
+static void
+decimal_check_errors (decContext *ctx)
+{
+  /* An error here could be a division by zero, an overflow, an underflow or
+     an invalid operation (from the DEC_Errors constant in decContext.h).
+     Since GDB doesn't complain about division by zero, overflow or underflow
+     errors for binary floating, we won't complain about them for decimal
+     floating either.  */
+  if (ctx->status & DEC_IEEE_854_Invalid_operation)
+    {
+      /* Leave only the error bits in the status flags.  */
+      ctx->status &= DEC_IEEE_854_Invalid_operation;
+      error (_("Cannot perform operation: %s"),
+            decContextStatusToString (ctx));
+    }
+}
+
+/* Helper function to convert from libdecnumber's appropriate representation
+   for computation to each size of decimal float.  */
+static void
+decimal_from_number (const decNumber *from,
+                    gdb_byte *to, const struct type *type)
+{
+  gdb_byte dec[16];
+
+  decContext set;
+
+  set_decnumber_context (&set, type);
+
+  switch (TYPE_LENGTH (type))
+    {
+      case 4:
+       decimal32FromNumber ((decimal32 *) dec, from, &set);
+       break;
+      case 8:
+       decimal64FromNumber ((decimal64 *) dec, from, &set);
+       break;
+      case 16:
+       decimal128FromNumber ((decimal128 *) dec, from, &set);
+       break;
+      default:
+       error (_("Unknown decimal floating point type."));
+       break;
+    }
+
+  match_endianness (dec, type, to);
+}
+
+/* Helper function to convert each size of decimal float to libdecnumber's
+   appropriate representation for computation.  */
+static void
+decimal_to_number (const gdb_byte *addr, const struct type *type,
+                  decNumber *to)
+{
+  gdb_byte dec[16];
+  match_endianness (addr, type, dec);
+
+  switch (TYPE_LENGTH (type))
+    {
+      case 4:
+       decimal32ToNumber ((decimal32 *) dec, to);
+       break;
+      case 8:
+       decimal64ToNumber ((decimal64 *) dec, to);
+       break;
+      case 16:
+       decimal128ToNumber ((decimal128 *) dec, to);
+       break;
+      default:
+       error (_("Unknown decimal floating point type."));
+       break;
+    }
+}
+
+/* Returns true if ADDR (which is of type TYPE) is the number zero.  */
+static bool
+decimal_is_zero (const gdb_byte *addr, const struct type *type)
+{
+  decNumber number;
+
+  decimal_to_number (addr, type, &number);
+
+  return decNumberIsZero (&number);
+}
+
+
+/* Implementation of target_float_ops using the libdecnumber decNumber type
+   as intermediate format.  */
+
+class decimal_float_ops : public target_float_ops
+{
+public:
+  std::string to_string (const gdb_byte *addr, const struct type *type,
+                        const char *format) const override;
+  bool from_string (gdb_byte *addr, const struct type *type,
+                   const std::string &string) const override;
+
+  LONGEST to_longest (const gdb_byte *addr,
+                     const struct type *type) const override;
+  void from_longest (gdb_byte *addr, const struct type *type,
+                    LONGEST val) const override;
+  void from_ulongest (gdb_byte *addr, const struct type *type,
+                     ULONGEST val) const override;
+  double to_host_double (const gdb_byte *addr,
+                        const struct type *type) const override
+  {
+    /* We don't support conversions between target decimal floating-point
+       types and the host double type.  */
+    gdb_assert_not_reached ("invalid operation on decimal float");
+  }
+  void from_host_double (gdb_byte *addr, const struct type *type,
+                        double val) const override
+  {
+    /* We don't support conversions between target decimal floating-point
+       types and the host double type.  */
+    gdb_assert_not_reached ("invalid operation on decimal float");
+  }
+  void convert (const gdb_byte *from, const struct type *from_type,
+               gdb_byte *to, const struct type *to_type) const override;
+
+  void binop (enum exp_opcode opcode,
+             const gdb_byte *x, const struct type *type_x,
+             const gdb_byte *y, const struct type *type_y,
+             gdb_byte *res, const struct type *type_res) const override;
+  int compare (const gdb_byte *x, const struct type *type_x,
+              const gdb_byte *y, const struct type *type_y) const override;
+};
+
+/* Convert decimal type to its string representation.  LEN is the length
+   of the decimal type, 4 bytes for decimal32, 8 bytes for decimal64 and
+   16 bytes for decimal128.  */
+std::string
+decimal_float_ops::to_string (const gdb_byte *addr, const struct type *type,
+                             const char *format = nullptr) const
+{
+  gdb_byte dec[16];
+
+  match_endianness (addr, type, dec);
+
+  if (format != nullptr)
+    {
+      /* We don't handle format strings (yet).  If the host printf supports
+        decimal floating point types, just use this.  Otherwise, fall back
+        to printing the number while ignoring the format string.  */
+#if defined (PRINTF_HAS_DECFLOAT)
+      /* FIXME: This makes unwarranted assumptions about the host ABI!  */
+      return string_printf (format, dec);
+#endif
+    }
+
+  std::string result;
+  result.resize (MAX_DECIMAL_STRING);
+
+  switch (TYPE_LENGTH (type))
+    {
+      case 4:
+       decimal32ToString ((decimal32 *) dec, &result[0]);
+       break;
+      case 8:
+       decimal64ToString ((decimal64 *) dec, &result[0]);
+       break;
+      case 16:
+       decimal128ToString ((decimal128 *) dec, &result[0]);
+       break;
+      default:
+       error (_("Unknown decimal floating point type."));
+       break;
+    }
+
+  return result;
+}
+
+/* Convert the string form of a decimal value to its decimal representation.
+   LEN is the length of the decimal type, 4 bytes for decimal32, 8 bytes for
+   decimal64 and 16 bytes for decimal128.  */
+bool
+decimal_float_ops::from_string (gdb_byte *addr, const struct type *type,
+                               const std::string &string) const
+{
+  decContext set;
+  gdb_byte dec[16];
+
+  set_decnumber_context (&set, type);
+
+  switch (TYPE_LENGTH (type))
+    {
+      case 4:
+       decimal32FromString ((decimal32 *) dec, string.c_str (), &set);
+       break;
+      case 8:
+       decimal64FromString ((decimal64 *) dec, string.c_str (), &set);
+       break;
+      case 16:
+       decimal128FromString ((decimal128 *) dec, string.c_str (), &set);
+       break;
+      default:
+       error (_("Unknown decimal floating point type."));
+       break;
+    }
+
+  match_endianness (dec, type, addr);
+
+  /* Check for errors in the DFP operation.  */
+  decimal_check_errors (&set);
+
+  return true;
+}
+
+/* Converts a LONGEST to a decimal float of specified LEN bytes.  */
+void
+decimal_float_ops::from_longest (gdb_byte *addr, const struct type *type,
+                                LONGEST from) const
+{
+  decNumber number;
+
+  if ((int32_t) from != from)
+    /* libdecnumber can convert only 32-bit integers.  */
+    error (_("Conversion of large integer to a "
+            "decimal floating type is not supported."));
+
+  decNumberFromInt32 (&number, (int32_t) from);
+
+  decimal_from_number (&number, addr, type);
+}
+
+/* Converts a ULONGEST to a decimal float of specified LEN bytes.  */
+void
+decimal_float_ops::from_ulongest (gdb_byte *addr, const struct type *type,
+                                 ULONGEST from) const
+{
+  decNumber number;
+
+  if ((uint32_t) from != from)
+    /* libdecnumber can convert only 32-bit integers.  */
+    error (_("Conversion of large integer to a "
+            "decimal floating type is not supported."));
+
+  decNumberFromUInt32 (&number, (uint32_t) from);
+
+  decimal_from_number (&number, addr, type);
+}
+
+/* Converts a decimal float of LEN bytes to a LONGEST.  */
+LONGEST
+decimal_float_ops::to_longest (const gdb_byte *addr,
+                              const struct type *type) const
+{
+  /* libdecnumber has a function to convert from decimal to integer, but
+     it doesn't work when the decimal number has a fractional part.  */
+  std::string str = to_string (addr, type);
+  return strtoll (str.c_str (), NULL, 10);
+}
+
+/* Perform operation OP with operands X and Y with sizes LEN_X and LEN_Y
+   and byte orders BYTE_ORDER_X and BYTE_ORDER_Y, and store value in
+   RESULT with size LEN_RESULT and byte order BYTE_ORDER_RESULT.  */
+void
+decimal_float_ops::binop (enum exp_opcode op,
+                         const gdb_byte *x, const struct type *type_x,
+                         const gdb_byte *y, const struct type *type_y,
+                         gdb_byte *res, const struct type *type_res) const
+{
+  decContext set;
+  decNumber number1, number2, number3;
+
+  decimal_to_number (x, type_x, &number1);
+  decimal_to_number (y, type_y, &number2);
+
+  set_decnumber_context (&set, type_res);
+
+  switch (op)
+    {
+      case BINOP_ADD:
+       decNumberAdd (&number3, &number1, &number2, &set);
+       break;
+      case BINOP_SUB:
+       decNumberSubtract (&number3, &number1, &number2, &set);
+       break;
+      case BINOP_MUL:
+       decNumberMultiply (&number3, &number1, &number2, &set);
+       break;
+      case BINOP_DIV:
+       decNumberDivide (&number3, &number1, &number2, &set);
+       break;
+      case BINOP_EXP:
+       decNumberPower (&number3, &number1, &number2, &set);
+       break;
+     default:
+       error (_("Operation not valid for decimal floating point number."));
+       break;
+    }
+
+  /* Check for errors in the DFP operation.  */
+  decimal_check_errors (&set);
+
+  decimal_from_number (&number3, res, type_res);
+}
+
+/* Compares two numbers numerically.  If X is less than Y then the return value
+   will be -1.  If they are equal, then the return value will be 0.  If X is
+   greater than the Y then the return value will be 1.  */
+int
+decimal_float_ops::compare (const gdb_byte *x, const struct type *type_x,
+                           const gdb_byte *y, const struct type *type_y) const
+{
+  decNumber number1, number2, result;
+  decContext set;
+  const struct type *type_result;
+
+  decimal_to_number (x, type_x, &number1);
+  decimal_to_number (y, type_y, &number2);
+
+  /* Perform the comparison in the larger of the two sizes.  */
+  type_result = TYPE_LENGTH (type_x) > TYPE_LENGTH (type_y) ? type_x : type_y;
+  set_decnumber_context (&set, type_result);
+
+  decNumberCompare (&result, &number1, &number2, &set);
+
+  /* Check for errors in the DFP operation.  */
+  decimal_check_errors (&set);
+
+  if (decNumberIsNaN (&result))
+    error (_("Comparison with an invalid number (NaN)."));
+  else if (decNumberIsZero (&result))
+    return 0;
+  else if (decNumberIsNegative (&result))
+    return -1;
+  else
+    return 1;
+}
+
+/* Convert a decimal value from a decimal type with LEN_FROM bytes to a
+   decimal type with LEN_TO bytes.  */
+void
+decimal_float_ops::convert (const gdb_byte *from, const struct type *from_type,
+                           gdb_byte *to, const struct type *to_type) const
+{
+  decNumber number;
+
+  decimal_to_number (from, from_type, &number);
+  decimal_from_number (&number, to, to_type);
+}
 
 
 /* Typed floating-point routines.  These routines operate on floating-point
    "struct type", which may be either a binary or decimal floating-point
    type (TYPE_CODE_FLT or TYPE_CODE_DECFLOAT).  */
 
+/* Return whether TYPE1 and TYPE2 are of the same category (binary or
+   decimal floating-point).  */
+static bool
+target_float_same_category_p (const struct type *type1,
+                             const struct type *type2)
+{
+  return type1->code () == type2->code ();
+}
+
+/* Return whether TYPE1 and TYPE2 use the same floating-point format.  */
+static bool
+target_float_same_format_p (const struct type *type1,
+                           const struct type *type2)
+{
+  if (!target_float_same_category_p (type1, type2))
+    return false;
+
+  switch (type1->code ())
+    {
+      case TYPE_CODE_FLT:
+       return floatformat_from_type (type1) == floatformat_from_type (type2);
+
+      case TYPE_CODE_DECFLOAT:
+       return (TYPE_LENGTH (type1) == TYPE_LENGTH (type2)
+               && (type_byte_order (type1)
+                   == type_byte_order (type2)));
+
+      default:
+       gdb_assert_not_reached ("unexpected type code");
+    }
+}
+
+/* Return the size (without padding) of the target floating-point
+   format used by TYPE.  */
+static int
+target_float_format_length (const struct type *type)
+{
+  switch (type->code ())
+    {
+      case TYPE_CODE_FLT:
+       return floatformat_totalsize_bytes (floatformat_from_type (type));
+
+      case TYPE_CODE_DECFLOAT:
+       return TYPE_LENGTH (type);
+
+      default:
+       gdb_assert_not_reached ("unexpected type code");
+    }
+}
+
+/* Identifiers of available host-side intermediate formats.  These must
+   be sorted so the that the more "general" kinds come later.  */
+enum target_float_ops_kind
+{
+  /* Target binary floating-point formats that match a host format.  */
+  host_float = 0,
+  host_double,
+  host_long_double,
+  /* Any other target binary floating-point format.  */
+  binary,
+  /* Any target decimal floating-point format.  */
+  decimal
+};
+
+/* Given a target type TYPE, choose the best host-side intermediate format
+   to perform operations on TYPE in.  */
+static enum target_float_ops_kind
+get_target_float_ops_kind (const struct type *type)
+{
+  switch (type->code ())
+    {
+      case TYPE_CODE_FLT:
+       {
+         const struct floatformat *fmt = floatformat_from_type (type);
+
+         /* Binary floating-point formats matching a host format.  */
+         if (fmt == host_float_format)
+           return target_float_ops_kind::host_float;
+         if (fmt == host_double_format)
+           return target_float_ops_kind::host_double;
+         if (fmt == host_long_double_format)
+           return target_float_ops_kind::host_long_double;
+
+         /* Any other binary floating-point format.  */
+         return target_float_ops_kind::binary;
+       }
+
+      case TYPE_CODE_DECFLOAT:
+       {
+         /* Any decimal floating-point format.  */
+         return target_float_ops_kind::decimal;
+       }
+
+      default:
+       gdb_assert_not_reached ("unexpected type code");
+    }
+}
+
+/* Return target_float_ops to peform operations for KIND.  */
+static const target_float_ops *
+get_target_float_ops (enum target_float_ops_kind kind)
+{
+  switch (kind)
+    {
+      /* If the type format matches one of the host floating-point
+        types, use that type as intermediate format.  */
+      case target_float_ops_kind::host_float:
+       {
+         static host_float_ops<float> host_float_ops_float;
+         return &host_float_ops_float;
+       }
+
+      case target_float_ops_kind::host_double:
+       {
+         static host_float_ops<double> host_float_ops_double;
+         return &host_float_ops_double;
+       }
+
+      case target_float_ops_kind::host_long_double:
+       {
+         static host_float_ops<long double> host_float_ops_long_double;
+         return &host_float_ops_long_double;
+       }
+
+      /* For binary floating-point formats that do not match any host format,
+        use mpfr_t as intermediate format to provide precise target-floating
+        point emulation.  However, if the MPFR library is not available,
+        use the largest host floating-point type as intermediate format.  */
+      case target_float_ops_kind::binary:
+       {
+#ifdef HAVE_LIBMPFR
+         static mpfr_float_ops binary_float_ops;
+#else
+         static host_float_ops<long double> binary_float_ops;
+#endif
+         return &binary_float_ops;
+       }
+
+      /* For decimal floating-point types, always use the libdecnumber
+        decNumber type as intermediate format.  */
+      case target_float_ops_kind::decimal:
+       {
+         static decimal_float_ops decimal_float_ops;
+         return &decimal_float_ops;
+       }
+
+      default:
+       gdb_assert_not_reached ("unexpected target_float_ops_kind");
+    }
+}
+
+/* Given a target type TYPE, determine the best host-side intermediate format
+   to perform operations on TYPE in.  */
+static const target_float_ops *
+get_target_float_ops (const struct type *type)
+{
+  enum target_float_ops_kind kind = get_target_float_ops_kind (type);
+  return get_target_float_ops (kind);
+}
+
+/* The same for operations involving two target types TYPE1 and TYPE2.  */
+static const target_float_ops *
+get_target_float_ops (const struct type *type1, const struct type *type2)
+{
+  gdb_assert (type1->code () == type2->code ());
+
+  enum target_float_ops_kind kind1 = get_target_float_ops_kind (type1);
+  enum target_float_ops_kind kind2 = get_target_float_ops_kind (type2);
+
+  /* Given the way the kinds are sorted, we simply choose the larger one;
+     this will be able to hold values of either type.  */
+  return get_target_float_ops (std::max (kind1, kind2));
+}
+
 /* Return whether the byte-stream ADDR holds a valid value of
    floating-point type TYPE.  */
 bool
 target_float_is_valid (const gdb_byte *addr, const struct type *type)
 {
-  if (TYPE_CODE (type) == TYPE_CODE_FLT)
+  if (type->code () == TYPE_CODE_FLT)
     return floatformat_is_valid (floatformat_from_type (type), addr);
 
-  if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+  if (type->code () == TYPE_CODE_DECFLOAT)
     return true;
 
   gdb_assert_not_reached ("unexpected type code");
@@ -49,13 +2329,12 @@ target_float_is_valid (const gdb_byte *addr, const struct type *type)
 bool
 target_float_is_zero (const gdb_byte *addr, const struct type *type)
 {
-  if (TYPE_CODE (type) == TYPE_CODE_FLT)
+  if (type->code () == TYPE_CODE_FLT)
     return (floatformat_classify (floatformat_from_type (type), addr)
            == float_zero);
 
-  if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
-    return decimal_is_zero (addr, TYPE_LENGTH (type),
-                           gdbarch_byte_order (get_type_arch (type)));
+  if (type->code () == TYPE_CODE_DECFLOAT)
+    return decimal_is_zero (addr, type);
 
   gdb_assert_not_reached ("unexpected type code");
 }
@@ -66,15 +2345,33 @@ std::string
 target_float_to_string (const gdb_byte *addr, const struct type *type,
                        const char *format)
 {
-  if (TYPE_CODE (type) == TYPE_CODE_FLT)
-    return floatformat_to_string (floatformat_from_type (type), addr, format);
+  /* Unless we need to adhere to a specific format, provide special
+     output for special cases of binary floating-point numbers.  */
+  if (format == nullptr && type->code () == TYPE_CODE_FLT)
+    {
+      const struct floatformat *fmt = floatformat_from_type (type);
 
-  if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
-    return decimal_to_string (addr, TYPE_LENGTH (type),
-                             gdbarch_byte_order (get_type_arch (type)),
-                             format);
+      /* Detect invalid representations.  */
+      if (!floatformat_is_valid (fmt, addr))
+       return "<invalid float value>";
 
-  gdb_assert_not_reached ("unexpected type code");
+      /* Handle NaN and Inf.  */
+      enum float_kind kind = floatformat_classify (fmt, addr);
+      if (kind == float_nan)
+       {
+         const char *sign = floatformat_is_negative (fmt, addr)? "-" : "";
+         const char *mantissa = floatformat_mantissa (fmt, addr);
+         return string_printf ("%snan(0x%s)", sign, mantissa);
+       }
+      else if (kind == float_infinite)
+       {
+         const char *sign = floatformat_is_negative (fmt, addr)? "-" : "";
+         return string_printf ("%sinf", sign);
+       }
+    }
+
+  const target_float_ops *ops = get_target_float_ops (type);
+  return ops->to_string (addr, type, format);
 }
 
 /* Parse string STRING into a target floating-number of type TYPE and
@@ -83,17 +2380,124 @@ bool
 target_float_from_string (gdb_byte *addr, const struct type *type,
                          const std::string &string)
 {
-  /* Ensure possible padding bytes in the target buffer are zeroed out.  */
-  memset (addr, 0, TYPE_LENGTH (type));
+  const target_float_ops *ops = get_target_float_ops (type);
+  return ops->from_string (addr, type, string);
+}
 
-  if (TYPE_CODE (type) == TYPE_CODE_FLT)
-    return floatformat_from_string (floatformat_from_type (type), addr,
-                                   string);
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to an integer value (rounding towards zero).  */
+LONGEST
+target_float_to_longest (const gdb_byte *addr, const struct type *type)
+{
+  const target_float_ops *ops = get_target_float_ops (type);
+  return ops->to_longest (addr, type);
+}
 
-  if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
-    return decimal_from_string (addr, TYPE_LENGTH (type),
-                               gdbarch_byte_order (get_type_arch (type)),
-                               string);
+/* Convert signed integer VAL to a target floating-number of type TYPE
+   and store it as byte-stream ADDR.  */
+void
+target_float_from_longest (gdb_byte *addr, const struct type *type,
+                          LONGEST val)
+{
+  const target_float_ops *ops = get_target_float_ops (type);
+  ops->from_longest (addr, type, val);
+}
 
-  gdb_assert_not_reached ("unexpected type code");
+/* Convert unsigned integer VAL to a target floating-number of type TYPE
+   and store it as byte-stream ADDR.  */
+void
+target_float_from_ulongest (gdb_byte *addr, const struct type *type,
+                           ULONGEST val)
+{
+  const target_float_ops *ops = get_target_float_ops (type);
+  ops->from_ulongest (addr, type, val);
+}
+
+/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
+   to a floating-point value in the host "double" format.  */
+double
+target_float_to_host_double (const gdb_byte *addr,
+                            const struct type *type)
+{
+  const target_float_ops *ops = get_target_float_ops (type);
+  return ops->to_host_double (addr, type);
+}
+
+/* Convert floating-point value VAL in the host "double" format to a target
+   floating-number of type TYPE and store it as byte-stream ADDR.  */
+void
+target_float_from_host_double (gdb_byte *addr, const struct type *type,
+                              double val)
+{
+  const target_float_ops *ops = get_target_float_ops (type);
+  ops->from_host_double (addr, type, val);
 }
+
+/* Convert a floating-point number of type FROM_TYPE from the target
+   byte-stream FROM to a floating-point number of type TO_TYPE, and
+   store it to the target byte-stream TO.  */
+void
+target_float_convert (const gdb_byte *from, const struct type *from_type,
+                     gdb_byte *to, const struct type *to_type)
+{
+  /* We cannot directly convert between binary and decimal floating-point
+     types, so go via an intermediary string.  */
+  if (!target_float_same_category_p (from_type, to_type))
+    {
+      std::string str = target_float_to_string (from, from_type);
+      target_float_from_string (to, to_type, str);
+      return;
+    }
+
+  /* Convert between two different formats in the same category.  */
+  if (!target_float_same_format_p (from_type, to_type))
+  {
+    const target_float_ops *ops = get_target_float_ops (from_type, to_type);
+    ops->convert (from, from_type, to, to_type);
+    return;
+  }
+
+  /* The floating-point formats match, so we simply copy the data, ensuring
+     possible padding bytes in the target buffer are zeroed out.  */
+  memset (to, 0, TYPE_LENGTH (to_type));
+  memcpy (to, from, target_float_format_length (to_type));
+}
+
+/* Perform the binary operation indicated by OPCODE, using as operands the
+   target byte streams X and Y, interpreted as floating-point numbers of
+   types TYPE_X and TYPE_Y, respectively.  Convert the result to type
+   TYPE_RES and store it into the byte-stream RES.
+
+   The three types must either be all binary floating-point types, or else
+   all decimal floating-point types.  Binary and decimal floating-point
+   types cannot be mixed within a single operation.  */
+void
+target_float_binop (enum exp_opcode opcode,
+                   const gdb_byte *x, const struct type *type_x,
+                   const gdb_byte *y, const struct type *type_y,
+                   gdb_byte *res, const struct type *type_res)
+{
+  gdb_assert (target_float_same_category_p (type_x, type_res));
+  gdb_assert (target_float_same_category_p (type_y, type_res));
+
+  const target_float_ops *ops = get_target_float_ops (type_x, type_y);
+  ops->binop (opcode, x, type_x, y, type_y, res, type_res);
+}
+
+/* Compare the two target byte streams X and Y, interpreted as floating-point
+   numbers of types TYPE_X and TYPE_Y, respectively.  Return zero if X and Y
+   are equal, -1 if X is less than Y, and 1 otherwise.
+
+   The two types must either both be binary floating-point types, or else
+   both be decimal floating-point types.  Binary and decimal floating-point
+   types cannot compared directly against each other.  */
+int
+target_float_compare (const gdb_byte *x, const struct type *type_x,
+                     const gdb_byte *y, const struct type *type_y)
+{
+  gdb_assert (target_float_same_category_p (type_x, type_y));
+
+  const target_float_ops *ops = get_target_float_ops (type_x, type_y);
+  return ops->compare (x, type_x, y, type_y);
+}
+