* gdb.threads/gcore-thread.exp: Use gdb_gcore_cmd.
[deliverable/binutils-gdb.git] / gdb / dfp.c
index 67cee138b730700cb940f4a0f999e722de3d05d3..ae6204b5629f69a2ee547ace5e5aeb53526fc30c 100644 (file)
--- a/gdb/dfp.c
+++ b/gdb/dfp.c
@@ -1,6 +1,6 @@
 /* Decimal floating point support for GDB.
 
-   Copyright 2007 Free Software Foundation, Inc.
+   Copyright 2007-2012 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 "expression.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "dfp.h"
 
 /* The order of the following headers is important for making sure
    decNumber structure is large enough to hold decimal128 digits.  */
@@ -30,7 +34,8 @@
    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, int len, gdb_byte *to)
+match_endianness (const gdb_byte *from, int len, enum bfd_endian byte_order,
+                 gdb_byte *to)
 {
   int i;
 
@@ -40,7 +45,7 @@ match_endianness (const gdb_byte *from, int len, gdb_byte *to)
 #define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG
 #endif
 
-  if (gdbarch_byte_order (current_gdbarch) == OPPOSITE_BYTE_ORDER)
+  if (byte_order == OPPOSITE_BYTE_ORDER)
     for (i = 0; i < len; i++)
       to[i] = from[len - i - 1];
   else
@@ -50,15 +55,101 @@ match_endianness (const gdb_byte *from, int len, gdb_byte *to)
   return;
 }
 
+/* Helper function to get the appropriate libdecnumber context for each size
+   of decimal float.  */
+static void
+set_decnumber_context (decContext *ctx, int len)
+{
+  switch (len)
+    {
+      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, int len)
+{
+  decContext set;
+
+  set_decnumber_context (&set, len);
+
+  switch (len)
+    {
+      case 4:
+       decimal32FromNumber ((decimal32 *) to, from, &set);
+       break;
+      case 8:
+       decimal64FromNumber ((decimal64 *) to, from, &set);
+       break;
+      case 16:
+       decimal128FromNumber ((decimal128 *) to, from, &set);
+       break;
+    }
+}
+
+/* Helper function to convert each size of decimal float to libdecnumber's
+   appropriate representation for computation.  */
+static void
+decimal_to_number (const gdb_byte *from, int len, decNumber *to)
+{
+  switch (len)
+    {
+      case 4:
+       decimal32ToNumber ((decimal32 *) from, to);
+       break;
+      case 8:
+       decimal64ToNumber ((decimal64 *) from, to);
+       break;
+      case 16:
+       decimal128ToNumber ((decimal128 *) from, to);
+       break;
+      default:
+       error (_("Unknown decimal floating point type."));
+       break;
+    }
+}
+
 /* 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.  */
 void
-decimal_to_string (const gdb_byte *decbytes, int len, char *s)
+decimal_to_string (const gdb_byte *decbytes, int len,
+                  enum bfd_endian byte_order, char *s)
 {
   gdb_byte dec[16];
 
-  match_endianness (decbytes, len, dec);
+  match_endianness (decbytes, len, byte_order, dec);
+
   switch (len)
     {
       case 4:
@@ -71,7 +162,7 @@ decimal_to_string (const gdb_byte *decbytes, int len, char *s)
        decimal128ToString ((decimal128 *) dec, s);
        break;
       default:
-       error (_("Unknown decimal floating point type.\n"));
+       error (_("Unknown decimal floating point type."));
        break;
     }
 }
@@ -80,34 +171,214 @@ decimal_to_string (const gdb_byte *decbytes, int len, char *s)
    LEN is the length of the decimal type, 4 bytes for decimal32, 8 bytes for
    decimal64 and 16 bytes for decimal128.  */
 int
-decimal_from_string (gdb_byte *decbytes, int len, const char *string)
+decimal_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order,
+                    const char *string)
 {
   decContext set;
   gdb_byte dec[16];
 
+  set_decnumber_context (&set, len);
+
   switch (len)
     {
       case 4:
-       decContextDefault (&set, DEC_INIT_DECIMAL32);
-       set.traps = 0;
        decimal32FromString ((decimal32 *) dec, string, &set);
        break;
       case 8:
-       decContextDefault (&set, DEC_INIT_DECIMAL64);
-       set.traps = 0;
        decimal64FromString ((decimal64 *) dec, string, &set);
        break;
       case 16:
-       decContextDefault (&set, DEC_INIT_DECIMAL128);
-       set.traps = 0;
        decimal128FromString ((decimal128 *) dec, string, &set);
        break;
       default:
-       error (_("Unknown decimal floating point type.\n"));
+       error (_("Unknown decimal floating point type."));
        break;
     }
 
-  match_endianness (dec, len, decbytes);
+  match_endianness (dec, len, byte_order, decbytes);
+
+  /* Check for errors in the DFP operation.  */
+  decimal_check_errors (&set);
 
   return 1;
 }
+
+/* Converts a value of an integral type to a decimal float of
+   specified LEN bytes.  */
+void
+decimal_from_integral (struct value *from,
+                      gdb_byte *to, int len, enum bfd_endian byte_order)
+{
+  LONGEST l;
+  gdb_byte dec[16];
+  decNumber number;
+  struct type *type;
+
+  type = check_typedef (value_type (from));
+
+  if (TYPE_LENGTH (type) > 4)
+    /* libdecnumber can convert only 32-bit integers.  */
+    error (_("Conversion of large integer to a "
+            "decimal floating type is not supported."));
+
+  l = value_as_long (from);
+
+  if (TYPE_UNSIGNED (type))
+    decNumberFromUInt32 (&number, (unsigned int) l);
+  else
+    decNumberFromInt32 (&number, (int) l);
+
+  decimal_from_number (&number, dec, len);
+  match_endianness (dec, len, byte_order, to);
+}
+
+/* Converts a value of a float type to a decimal float of
+   specified LEN bytes.
+
+   This is an ugly way to do the conversion, but libdecnumber does
+   not offer a direct way to do it.  */
+void
+decimal_from_floating (struct value *from,
+                      gdb_byte *to, int len, enum bfd_endian byte_order)
+{
+  char *buffer;
+
+  buffer = xstrprintf ("%.30" DOUBLEST_PRINT_FORMAT, value_as_double (from));
+
+  decimal_from_string (to, len, byte_order, buffer);
+
+  xfree (buffer);
+}
+
+/* Converts a decimal float of LEN bytes to a double value.  */
+DOUBLEST
+decimal_to_doublest (const gdb_byte *from, int len, enum bfd_endian byte_order)
+{
+  char buffer[MAX_DECIMAL_STRING];
+
+  /* This is an ugly way to do the conversion, but libdecnumber does
+     not offer a direct way to do it.  */
+  decimal_to_string (from, len, byte_order, buffer);
+  return strtod (buffer, NULL);
+}
+
+/* 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_binop (enum exp_opcode op,
+              const gdb_byte *x, int len_x, enum bfd_endian byte_order_x,
+              const gdb_byte *y, int len_y, enum bfd_endian byte_order_y,
+              gdb_byte *result, int len_result,
+              enum bfd_endian byte_order_result)
+{
+  decContext set;
+  decNumber number1, number2, number3;
+  gdb_byte dec1[16], dec2[16], dec3[16];
+
+  match_endianness (x, len_x, byte_order_x, dec1);
+  match_endianness (y, len_y, byte_order_y, dec2);
+
+  decimal_to_number (dec1, len_x, &number1);
+  decimal_to_number (dec2, len_y, &number2);
+
+  set_decnumber_context (&set, len_result);
+
+  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:
+       internal_error (__FILE__, __LINE__,
+                       _("Unknown decimal floating point operation."));
+       break;
+    }
+
+  /* Check for errors in the DFP operation.  */
+  decimal_check_errors (&set);
+
+  decimal_from_number (&number3, dec3, len_result);
+
+  match_endianness (dec3, len_result, byte_order_result, result);
+}
+
+/* Returns true if X (which is LEN bytes wide) is the number zero.  */
+int
+decimal_is_zero (const gdb_byte *x, int len, enum bfd_endian byte_order)
+{
+  decNumber number;
+  gdb_byte dec[16];
+
+  match_endianness (x, len, byte_order, dec);
+  decimal_to_number (dec, len, &number);
+
+  return decNumberIsZero (&number);
+}
+
+/* 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_compare (const gdb_byte *x, int len_x, enum bfd_endian byte_order_x,
+                const gdb_byte *y, int len_y, enum bfd_endian byte_order_y)
+{
+  decNumber number1, number2, result;
+  decContext set;
+  gdb_byte dec1[16], dec2[16];
+  int len_result;
+
+  match_endianness (x, len_x, byte_order_x, dec1);
+  match_endianness (y, len_y, byte_order_y, dec2);
+
+  decimal_to_number (dec1, len_x, &number1);
+  decimal_to_number (dec2, len_y, &number2);
+
+  /* Perform the comparison in the larger of the two sizes.  */
+  len_result = len_x > len_y ? len_x : len_y;
+  set_decnumber_context (&set, len_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_convert (const gdb_byte *from, int len_from,
+                enum bfd_endian byte_order_from, gdb_byte *to, int len_to,
+                enum bfd_endian byte_order_to)
+{
+  decNumber number;
+  gdb_byte dec[16];
+
+  match_endianness (from, len_from, byte_order_from, dec);
+
+  decimal_to_number (dec, len_from, &number);
+  decimal_from_number (&number, dec, len_to);
+
+  match_endianness (dec, len_to, byte_order_to, to);
+}
This page took 0.027097 seconds and 4 git commands to generate.