From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23418 invoked by alias); 2 Jan 2008 20:07:06 -0000 Received: (qmail 23038 invoked by uid 22791); 2 Jan 2008 20:06:54 -0000 X-Spam-Check-By: sourceware.org Received: from igw2.br.ibm.com (HELO igw2.br.ibm.com) (32.104.18.25) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 02 Jan 2008 20:00:59 +0000 Received: from mailhub3.br.ibm.com (mailhub3 [9.18.232.110]) by igw2.br.ibm.com (Postfix) with ESMTP id 334BB17F471 for ; Wed, 2 Jan 2008 17:55:26 -0200 (BRDT) Received: from d24av01.br.ibm.com (d24av01.br.ibm.com [9.18.232.46]) by mailhub3.br.ibm.com (8.13.8/8.13.8/NCO v8.7) with ESMTP id m02K0kZt2601042 for ; Wed, 2 Jan 2008 18:00:47 -0200 Received: from d24av01.br.ibm.com (loopback [127.0.0.1]) by d24av01.br.ibm.com (8.12.11.20060308/8.13.3) with ESMTP id m02K0kgp001215 for ; Wed, 2 Jan 2008 18:00:46 -0200 Received: from [9.18.238.251] (dyn532128.br.ibm.com [9.18.238.251]) by d24av01.br.ibm.com (8.12.11.20060308/8.12.11) with ESMTP id m02K0k3k001212; Wed, 2 Jan 2008 18:00:46 -0200 Subject: Re: [patch 2/2] Wrap-up expression support for DFP. From: Thiago Jung Bauermann To: Eli Zaretskii Cc: gdb-patches@sourceware.org In-Reply-To: References: <20071220054926.148275471@br.ibm.com> <20071220055107.194393592@br.ibm.com> <1198705277.12907.39.camel@localhost.localdomain> Content-Type: multipart/mixed; boundary="=-2jqMC27Pg/EuOfxJsnTh" Date: Wed, 02 Jan 2008 20:07:00 -0000 Message-Id: <1199304046.12907.77.camel@localhost.localdomain> Mime-Version: 1.0 X-Mailer: Evolution 2.12.2 X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-01/txt/msg00029.txt.bz2 --=-2jqMC27Pg/EuOfxJsnTh Content-Type: text/plain Content-Transfer-Encoding: 7bit Content-length: 1548 On Sat, 2007-12-29 at 13:18 +0200, Eli Zaretskii wrote: > > > We have portable substitutes for snprintf, I think. Take a look at > > > libiberty, for example. > > > > Yes, it is there. I didn't find any .h, though, so I don't really know > > the proper way to use them. > > There's include/libiberty.h, where all libiberty functions are > declared, conditioned by corresponding preprocessor defines. Those > preprocessor defines should be defined by the configure script for > platforms that don't have those functions in their native libraries. Ok, since libiberty.h is already included by defs.h, I don't need to worry about it. Thanks for the explanation. > > > > + /* 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, buffer); > > > > + return atof (buffer); > > > > > > Isn't strtold better here? > > > > It's C99 and not in libiberty. > > Then how about using strtod? Fine by me. Thanks for your review, Eli. The attached patch has the following changes from the one I originally posted: - includes documentation update to GDB manual, under the "C and C++" subsection; - uses asprintf instead of sprintf, with "%.30Lg" in decimal_from_floating; - uses strtod instead of atof in decimal_to_double; - decimal/binary FP conversion tests now accept some rounding variation: gdb_test "p (_Decimal64) 3.1" " = 3.(0999.*|1000.*)" Is this version ok? -- []'s Thiago Jung Bauermann Software Engineer IBM Linux Technology Center --=-2jqMC27Pg/EuOfxJsnTh Content-Disposition: attachment; filename=expressions.diff Content-Type: text/x-patch; name=expressions.diff; charset=utf-8 Content-Transfer-Encoding: 7bit Content-length: 40786 gdb/ 2008-01-02 Thiago Jung Bauermann * Makefile.in (dfp.o): Depend on expression.h, gdbtypes.h and value.h. (valarith.o): Depend on dfp.h. (valops.o): Likewise. * dfp.c: Include expression.h, gdbtypes.h, value.h and dfp.h. (set_decnumber_context): New function. (decimal_check_errors): Likewise. (decimal_from_number): Likewise. (decimal_to_number): Likewise. (decimal_from_string): Use set_decnumber_context and decimal_check_errors. (decimal_from_integral): New function. (decimal_from_floating): Likewise. (decimal_to_double): Likewise. (promote_decimal): Likewise. (decimal_binop): Likewise. (decimal_is_zero): Likewise. (decimal_compare): Likewise. (decimal_convert): Likewise. * dfp.h (decimal_from_integral): New prototype. (decimal_from_floating): Likewise. (decimal_to_double): Likewise. (decimal_binop): Likewise. (decimal_is_zero): Likewise. (decimal_compare): Likewise. (decimal_convert): Likewise. * eval.c (evaluate_subexp_standard): Remove expect_type argument from call to value_from_decfloat. * valarith.c: Include dfp.h. (value_args_as_decimal): New function. (value_binop): Add if block to handle TYPE_CODE_DECFLOAT values. (value_logical_not): Likewise. (value_equal): Likewise. (value_less): Likewise. (value_pos): Likewise. (value_neg): Formatting fix. * valops.c: Include dfp.h. (value_cast): Add if block to handle TYPE_CODE_DECFLOAT values. * value.c (unpack_long): Add case to handle TYPE_CODE_DECFLOAT. (unpack_double): Add if block to handle TYPE_CODE_DECFLOAT. (value_from_decfloat): Remove expect_type argument. * value.h (value_from_decfloat): Update prototype. gdb/testsuite/ 2008-01-02 Thiago Jung Bauermann * gdb.base/dfp-exprs.exp (test_dfp_arithmetic_expressions): Add tests for expressions with decimal float values. (test_dfp_conversions): New function to test casts to and from decimal float types. Call test_dfp_conversions. * gdb.base/dfp-test.c (struct decstruct): Add float4 and double8 elements. (main): Initialize ds.float4 and ds.double8 elements. * gdb.base/dfp-test.exp (d32_set_tests): Fix typo. Adjust expect string to new error message. (d64_set_tests): Likewise. (d128_set_tests): Likewise. Add tests for expressions with decimal float variables. Add tests for conversions to and from decimal float types. gdb/doc 2008-01-02 Thiago Jung Bauermann * gdb.texinfo (C and C++): Add Decimal Floating Point format subsubsection. (Decimal Floating Point format): New subsubsection. Index: src-git/gdb/Makefile.in =================================================================== --- src-git.orig/gdb/Makefile.in 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/Makefile.in 2007-12-26 18:59:27.000000000 -0200 @@ -2046,7 +2046,8 @@ dsrec.o: dsrec.c $(defs_h) $(serial_h) $ dummy-frame.o: dummy-frame.c $(defs_h) $(dummy_frame_h) $(regcache_h) \ $(frame_h) $(inferior_h) $(gdb_assert_h) $(frame_unwind_h) \ $(command_h) $(gdbcmd_h) $(gdb_string_h) -dfp.o: dfp.c $(defs_h) $(dfp_h) $(decimal128_h) $(decimal64_h) $(decimal32_h) +dfp.o: dfp.c $(defs_h) $(expression_h) $(gdbtypes_h) $(value_h) $(dfp_h) \ + $(decimal128_h) $(decimal64_h) $(decimal32_h) dwarf2expr.o: dwarf2expr.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(value_h) \ $(gdbcore_h) $(elf_dwarf2_h) $(dwarf2expr_h) dwarf2-frame.o: dwarf2-frame.c $(defs_h) $(dwarf2expr_h) $(elf_dwarf2_h) \ @@ -2910,12 +2911,12 @@ v850-tdep.o: v850-tdep.c $(defs_h) $(fra $(regcache_h) $(dis_asm_h) $(osabi_h) valarith.o: valarith.c $(defs_h) $(value_h) $(symtab_h) $(gdbtypes_h) \ $(expression_h) $(target_h) $(language_h) $(gdb_string_h) \ - $(doublest_h) $(infcall_h) + $(doublest_h) $(dfp_h) $(infcall_h) valops.o: valops.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(value_h) $(frame_h) \ $(inferior_h) $(gdbcore_h) $(target_h) $(demangle_h) $(language_h) \ $(gdbcmd_h) $(regcache_h) $(cp_abi_h) $(block_h) $(infcall_h) \ $(dictionary_h) $(cp_support_h) $(gdb_string_h) $(gdb_assert_h) \ - $(cp_support_h) $(observer_h) + $(cp_support_h) $(observer_h) $(dfp_h) valprint.o: valprint.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(value_h) $(gdbcore_h) $(gdbcmd_h) $(target_h) $(language_h) \ $(annotate_h) $(valprint_h) $(floatformat_h) $(doublest_h) \ Index: src-git/gdb/dfp.c =================================================================== --- src-git.orig/gdb/dfp.c 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/dfp.c 2008-01-02 15:59:08.000000000 -0200 @@ -18,6 +18,10 @@ along with this program. If not, see . */ #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. */ @@ -50,6 +54,89 @@ match_endianness (const gdb_byte *from, 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.\n")); + 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. */ @@ -59,6 +146,7 @@ decimal_to_string (const gdb_byte *decby gdb_byte dec[16]; match_endianness (decbytes, len, dec); + switch (len) { case 4: @@ -85,21 +173,17 @@ decimal_from_string (gdb_byte *decbytes, 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: @@ -109,5 +193,207 @@ decimal_from_string (gdb_byte *decbytes, match_endianness (dec, len, 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) +{ + 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, 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) +{ + char *buffer; + int ret; + + ret = asprintf (&buffer, "%.30Lg", value_as_double (from)); + if (ret == -1) + error (_("Error in conversion to decimal float.")); + + decimal_from_string (to, len, buffer); + + free (buffer); +} + +/* Converts a decimal float of LEN bytes to a double value. */ +DOUBLEST +decimal_to_double (const gdb_byte *from, int len) +{ + 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, buffer); + return strtod (buffer, NULL); +} + +/* Check if operands have the same size and convert them to the + biggest of the two if necessary. */ +static int +promote_decimal (gdb_byte *x, int len_x, gdb_byte *y, int len_y) +{ + int len_result; + decNumber number; + + if (len_x < len_y) + { + decimal_to_number (x, len_x, &number); + decimal_from_number (&number, x, len_y); + len_result = len_y; + } + else if (len_x > len_y) + { + decimal_to_number (y, len_y, &number); + decimal_from_number (&number, y, len_x); + len_result = len_x; + } + else + len_result = len_x; + + return len_result; +} + +/* Perform operation OP with operands X and Y and store value in RESULT. + If LEN_X and LEN_Y are not equal, RESULT will have the size of the biggest + of the two, and LEN_RESULT will be set accordingly. */ +void +decimal_binop (enum exp_opcode op, const gdb_byte *x, int len_x, + const gdb_byte *y, int len_y, gdb_byte *result, int *len_result) +{ + decContext set; + decNumber number1, number2, number3; + gdb_byte dec1[16], dec2[16], dec3[16]; + + match_endianness (x, len_x, dec1); + match_endianness (y, len_y, dec2); + + *len_result = promote_decimal (dec1, len_x, dec2, len_y); + + /* Both operands are of size *len_result from now on. */ + + decimal_to_number (dec1, *len_result, &number1); + decimal_to_number (dec2, *len_result, &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: + error (_("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, result); +} + +/* Returns true if X (which is LEN bytes wide) is the number zero. */ +int +decimal_is_zero (const gdb_byte *x, int len) +{ + decNumber number; + gdb_byte dec[16]; + + match_endianness (x, len, 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, const gdb_byte *y, int len_y) +{ + decNumber number1, number2, result; + decContext set; + gdb_byte dec1[16], dec2[16]; + int len_result; + + match_endianness (x, len_x, dec1); + match_endianness (y, len_y, dec2); + + len_result = promote_decimal (dec1, len_x, dec2, len_y); + + decimal_to_number (dec1, len_result, &number1); + decimal_to_number (dec2, len_result, &number2); + + 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, gdb_byte *to, + int len_to) +{ + decNumber number; + + decimal_to_number (from, len_from, &number); + decimal_from_number (&number, to, len_to); +} Index: src-git/gdb/dfp.h =================================================================== --- src-git.orig/gdb/dfp.h 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/dfp.h 2007-12-26 18:59:27.000000000 -0200 @@ -31,5 +31,14 @@ extern void decimal_to_string (const gdb_byte *, int, char *); extern int decimal_from_string (gdb_byte *, int, const char *); +extern void decimal_from_integral (struct value *from, gdb_byte *to, int len); +extern void decimal_from_floating (struct value *from, gdb_byte *to, int len); +extern DOUBLEST decimal_to_double (const gdb_byte *from, int len); +extern void decimal_binop (enum exp_opcode, const gdb_byte *, int, + const gdb_byte *, int, gdb_byte *, int *); +extern int decimal_is_zero (const gdb_byte *x, int len); +extern int decimal_compare (const gdb_byte *x, int len_x, const gdb_byte *y, int len_y); +extern void decimal_convert (const gdb_byte *from, int len_from, gdb_byte *to, + int len_to); #endif Index: src-git/gdb/eval.c =================================================================== --- src-git.orig/gdb/eval.c 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/eval.c 2007-12-26 18:59:27.000000000 -0200 @@ -458,8 +458,8 @@ evaluate_subexp_standard (struct type *e case OP_DECFLOAT: (*pos) += 3; - return value_from_decfloat (expect_type, exp->elts[pc + 1].type, - exp->elts[pc + 2].decfloatconst); + return value_from_decfloat (exp->elts[pc + 1].type, + exp->elts[pc + 2].decfloatconst); case OP_VAR_VALUE: (*pos) += 3; Index: src-git/gdb/testsuite/gdb.base/dfp-exprs.exp =================================================================== --- src-git.orig/gdb/testsuite/gdb.base/dfp-exprs.exp 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/testsuite/gdb.base/dfp-exprs.exp 2008-01-02 17:43:58.000000000 -0200 @@ -74,14 +74,102 @@ proc test_dfp_literals_accepted {} { proc test_dfp_arithmetic_expressions {} { -# Arithmetic operations for DFP types are not yet implemented in GDB. -# These tests are to verify that they will generate expected error messages. + # _Decimal32 operands. + gdb_test "p 1.4df + 1.2df" " = 2.6" + gdb_test "p 1.4df - 1.2df" " = 0.2" + gdb_test "p 1.4df * 1.2df" " = 1.68" + gdb_test "p 1.4df / 1.2df" " = 1.166667" + + # _Decimal64 operands. + gdb_test "p 1.4dd + 1.2dd" " = 2.6" + gdb_test "p 1.4dd - 1.2dd" " = 0.2" + gdb_test "p 1.4dd * 1.2dd" " = 1.68" + gdb_test "p 1.4dd / 1.2dd" " = 1.166666666666667" + + # _Decimal128 operands. + gdb_test "p 1.4dl + 1.2dl" " = 2.6" + gdb_test "p 1.4dl - 1.2dl" " = 0.2" + gdb_test "p 1.4dl * 1.2dl" " = 1.68" + gdb_test "p 1.4dl / 1.2dl" " = 1.166666666666666666666666666666667" + + # Test type of operation result. + gdb_test "ptype 2.df + 2.df" "= _Decimal32" + gdb_test "ptype 2.dd + 2.dd" "= _Decimal64" + gdb_test "ptype 2.dl + 2.dl" "= _Decimal128" + + # Mixture of different _Decimal sizes. + gdb_test "p 2.1df + 2.7dd" "= 4.8" + gdb_test "p 2.1dd + 2.7df" "= 4.8" + gdb_test "p 2.6df + 2.7dl" "= 5.3" + gdb_test "p 2.6dl + 2.7df" "= 5.3" + gdb_test "p 2.3dd + 2.2dl" "= 4.5" + gdb_test "p 2.3dl + 2.2dd" "= 4.5" + gdb_test "ptype 2.df + 2.dd" "= _Decimal64" + gdb_test "ptype 2.df + 2.dl" "= _Decimal128" + gdb_test "ptype 2.dd + 2.dl" "= _Decimal128" + + # Mixture of Decimal and integral operands + gdb_test "p 1.2df + 1" " = 2.2" + gdb_test "p 2 + 1.7dd" " = 3.7" + gdb_test "p 3 + 2.1dl" " = 5.1" + gdb_test "ptype 1.2df + 1" " = _Decimal32" + gdb_test "ptype 2 + 1.7dd" " = _Decimal64" + gdb_test "ptype 3 + 2.1dl" " = _Decimal128" + + # Reject operation with integral larger than 32-bits + gdb_test "p 1.2df + 2ll" "Conversion of large integer to a decimal floating type is not supported." + + # Reject operation with DFP and Binary FP + gdb_test "p 1.2df + 1.2f" "Mixing decimal floating types with other floating types is not allowed." + + # Test other operations with DFP operands + gdb_test "p !0.df" " = 1" + gdb_test "p !0.dd" " = 1" + gdb_test "p !0.dl" " = 1" + gdb_test "p !0.5df" " = 0" + gdb_test "p !0.5dd" " = 0" + gdb_test "p !0.5dl" " = 0" + + gdb_test "p 1.2df == 1.2df" " = 1" + gdb_test "p 1.2df == 1.2dd" " = 1" + gdb_test "p 1.2df == 1.2dl" " = 1" + gdb_test "p 1.2dd == 1.2df" " = 1" + gdb_test "p 1.2dd == 1.2dl" " = 1" + gdb_test "p 1.2dl == 1.2df" " = 1" + gdb_test "p 1.2dl == 1.2dd" " = 1" + gdb_test "p 1.2df == 1.3df" " = 0" + gdb_test "p 1.2df == 1.3dd" " = 0" + gdb_test "p 1.2df == 1.3dl" " = 0" + gdb_test "p 1.2dd == 1.3df" " = 0" + gdb_test "p 1.2dd == 1.3dl" " = 0" + gdb_test "p 1.2dl == 1.3df" " = 0" + gdb_test "p 1.2dl == 1.3dd" " = 0" + + gdb_test "p +1.2df" " = 1.2" + gdb_test "p +1.2dd" " = 1.2" + gdb_test "p +1.2dl" " = 1.2" + + gdb_test "p 1.2df < 1.3df" " = 1" + gdb_test "p 1.2df < 1.3dd" " = 1" + gdb_test "p 1.2dl < 1.3df" " = 1" + gdb_test "p 1.2dd < 1.3dd" " = 1" + gdb_test "p 1.2dd < 1.3dl" " = 1" + gdb_test "p 1.2dl < 1.3dl" " = 1" + gdb_test "p 1.2dl < 1.3df" " = 1" + gdb_test "p 1.2df > 1" " = 1" + gdb_test "p 1.2dl > 2" " = 0" + gdb_test "p 2 > 1.2dd" " = 1" + gdb_test "p 2 > 3.1dl" " = 0" +} - gdb_test "p 1.4df + 1.2df" "Argument to arithmetic operation not a number or boolean.*" - gdb_test "p 1.4df - 1.2df" ".*Argument to arithmetic operation not a number or boolean.*" - gdb_test "p 1.4df * 1.2df" "Argument to arithmetic operation not a number or boolean.*" - gdb_test "p 1.4df / 1.2df" "Argument to arithmetic operation not a number or boolean.*" +proc test_dfp_conversions {} { + # Test cast to and from DFP values, and between DFPs of different sizes + gdb_test "p (float) -0.1df" " = -0.(0999.*|1000.*)" + gdb_test "p (int) 8.3dd" " = 8" + gdb_test "p (_Decimal64) 3.1" " = 3.(0999.*|1000.*)" + gdb_test "p (_Decimal128) 3.7df" " = 3.7" + gdb_test "p (_Decimal32) 4" " = 4" } # Start with a fresh gdb. @@ -92,3 +180,4 @@ gdb_reinitialize_dir $srcdir/$subdir test_dfp_literals_accepted test_dfp_arithmetic_expressions +test_dfp_conversions Index: src-git/gdb/testsuite/gdb.base/dfp-test.c =================================================================== --- src-git.orig/gdb/testsuite/gdb.base/dfp-test.c 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/testsuite/gdb.base/dfp-test.c 2007-12-26 18:59:27.000000000 -0200 @@ -26,6 +26,8 @@ struct decstruct { int int4; long long8; + float float4; + double double8; _Decimal32 dec32; _Decimal64 dec64; _Decimal128 dec128; @@ -85,6 +87,8 @@ int main() ds.int4 = 1; ds.long8 = 2; + ds.float4 = 3.1; + ds.double8 = 4.2; ds.dec32 = 1.2345df; ds.dec64 = 1.2345dd; ds.dec128 = 1.2345dl; Index: src-git/gdb/testsuite/gdb.base/dfp-test.exp =================================================================== --- src-git.orig/gdb/testsuite/gdb.base/dfp-test.exp 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/testsuite/gdb.base/dfp-test.exp 2008-01-02 17:33:40.000000000 -0200 @@ -18,6 +18,53 @@ # This file is part of the gdb testsuite. It is intended to test that # gdb could correctly handle decimal floating point introduced in IEEE 754R. +if $tracelevel then { + strace $tracelevel +} + +set testfile "dfp-test" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +# Try to compile the test case. If we can't, assume the +# toolchain does not yet provide DFP support and bail out. +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {quiet debug}] != "" } { + verbose "Skipping DFP tests." + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if ![runto_main] then { + perror "couldn't run to breakpoint" + continue +} + +# Detect the size of the target's basic types (from gdb.base/long_long.exp). + +proc get_valueof { fmt exp default } { + global gdb_prompt + send_gdb "print${fmt} ${exp}\n" + gdb_expect { + -re "\\$\[0-9\]* = (\[-\]*\[0-9\]*).*$gdb_prompt $" { + set val $expect_out(1,string) + } + timeout { + set val ${default} + } + } + return ${val} +} + +proc get_sizeof { type default } { + return [get_valueof "/d" "sizeof (${type})" $default] +} + +set sizeof_long [get_sizeof "long" 4] + proc d32_set_tests {} { gdb_test "p d32=123.45df" " = 123.45" @@ -43,10 +90,10 @@ proc d32_set_tests {} { gdb_test "p d32=1.234567E+97df" " = Infinity" "1.234567E+97 is Infinity" # Test that gdb could detect the errors in the string representation of _Decimal32 - gdb_test "p d32=12345.df" " = 12345" "12345. is an valid number" + gdb_test "p d32=12345.df" " = 12345" "12345. is a valid number" gdb_test "p d32=12345df" ".*Invalid number.*" "12345 is an invalid number" - gdb_test "p d32=1.23Edf" " = NaN" "1.23E is NaN (not a number)" - gdb_test "p d32=1.23E45Adf" " = NaN" "1.23E45A is NaN (not a number)" + gdb_test "p d32=1.23Edf" ".*Conversion syntax.*" "1.23E is an invalid number" + gdb_test "p d32=1.23E45Adf" ".*Conversion syntax.*" "1.23E45A is an invalid number" } proc d64_set_tests {} { @@ -75,8 +122,8 @@ proc d64_set_tests {} { # Test that gdb could detect the errors in the string representation of _Decimal64 gdb_test "p d64=12345dd" ".*Invalid number.*" "12345dd is an invalid number" - gdb_test "p d64=1.23Edd" " = NaN" "1.23E is NaN (not a number)" - gdb_test "p d64=1.23E45Add" "= NaN" "1.23E45A is NaN (not a number)" + gdb_test "p d64=1.23Edd" ".*Conversion syntax.*" "1.23E is an invalid number" + gdb_test "p d64=1.23E45Add" ".*Conversion syntax.*" "1.23E45A is an invalid number" } proc d128_set_tests {} { @@ -104,33 +151,8 @@ proc d128_set_tests {} { # Test that gdb could detect the errors in the string representation of _Decimal128 gdb_test "p d128=12345dl" ".*Invalid number.*" "12345dl is an invalid number" - gdb_test "p d128=1.23Edl" " = NaN" "1.23E is NaN (not a number)" - gdb_test "p d128=1.23E45Adl" "= NaN" "1.23E45A is NaN (not a number)" -} - - -if $tracelevel { - strace $tracelevel -} - -set testfile "dfp-test" -set srcfile ${testfile}.c -set binfile ${objdir}/${subdir}/${testfile} -# Try to compile the test case. If we can't, assume the -# toolchain does not yet provide DFP support and bail out. -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {quiet debug}] != "" } { - verbose "Skipping DFP tests." - return -1 -} - -gdb_exit -gdb_start -gdb_reinitialize_dir $srcdir/$subdir -gdb_load ${binfile} - -if ![runto_main] then { - perror "couldn't run to breakpoint" - continue + gdb_test "p d128=1.23Edl" ".*Conversion syntax.*" "1.23E is an invalid number" + gdb_test "p d128=1.23E45Adl" ".*Conversion syntax.*" "1.23E45A is an invalid number" } # Different tests on 32-bits decimal floating point, including the printing @@ -237,6 +259,57 @@ gdb_test "print ds.dec32" " = 1.2345" gdb_test "print ds.dec64" " = 1.2345" gdb_test "print ds.dec128" " = 1.2345" +# Test expressions with DFP variables. + +gdb_test "print d32 + ds.dec32" " = 1.3345" +gdb_test "print d64 + ds.dec64" " = 1.3345" +gdb_test "print d128 + ds.dec128" " = 1.3345" + +# Test conversion between different _Decimal sizes. + +gdb_test "ptype d64 + ds.dec32" " = volatile _Decimal64" +gdb_test "ptype d128 + ds.dec32" " = volatile _Decimal128" +gdb_test "ptype d128 + ds.dec64" " = volatile _Decimal128" + +# Mixture of Decimal and integral operands +gdb_test "p d32 + 1" " = 1.1" +gdb_test "p 2 + d64" " = 2.1" +gdb_test "p ds.int4 + d128" " = 1.1" +gdb_test "ptype d32 + 1" " = volatile _Decimal32" +gdb_test "ptype ds.int4 + d128" " = volatile _Decimal128" + +# Test other operations with DFP operands +gdb_test "p !d32" " = 0" +gdb_test "p !d64" " = 0" +gdb_test "p !d128" " = 0" +gdb_test "p +d32" " = 0.1" +gdb_test "p +d64" " = 0.1" +gdb_test "p +d128" " = 0.1" +gdb_test "p d64 == d128" " = 1" +gdb_test "p d128 == ds.dec32" " = 0" +gdb_test "p d128 == d32" " = 1" +gdb_test "p ds.dec32 == ds.dec64" " = 1" +gdb_test "p d32 < ds.dec32" " = 1" +gdb_test "p d64 < ds.dec64" " = 1" +gdb_test "p d128 < ds.dec128" " = 1" +gdb_test "p ds.dec32 < d32" " = 0" +gdb_test "p d64 > ds.dec64" " = 0" +gdb_test "p ds.dec128 > d128 " " = 1" +gdb_test "p d32 < ds.int4" " = 1" +gdb_test "p ds.int4 > d32" " = 1" +gdb_test "p ds.dec32 < ds.int4" " = 0" +gdb_test "p ds.int4 > ds.dec64" " = 0" +gdb_test "p ds.dec128 > ds.int4" " = 1" + +# Reject operation with integral larger than 32-bits +if { ${sizeof_long} > 4 } { + gdb_test "p d32 + ds.long8" "Conversion of large integer to a decimal floating type is not supported." +} + +# Reject operation with DFP and Binary FP +gdb_test "p d64 + ds.float4" "Mixing decimal floating types with other floating types is not allowed." +gdb_test "p ds.double8 + d128" "Mixing decimal floating types with other floating types is not allowed." + # The following tests are intended to verify that gdb can handle "d1=d2" # and "d1=-d2" correctly. @@ -246,3 +319,12 @@ gdb_test "print ds.dec128=d128" " = 0.1" gdb_test "print ds.dec32 = -d32" " = -0.1" gdb_test "print ds.dec64 = -d64" " = -0.1" gdb_test "print ds.dec128 = -d128" " = -0.1" + +# Test cast to and from DFP values + +gdb_test "print ds.double8 = ds.dec64" " = -0.(0999.*|1000.*)" +gdb_test "print ds.dec64 = ds.float4" " = 3.(0999.*|1000.*)" +gdb_test "print ds.dec128 = -ds.double8" " = 0.(0999.*|1000.*)" +gdb_test "print ds.dec128 = ds.dec32" " = -0.1" +gdb_test "print ds.dec32 = ds.int4" " = 1" +gdb_test "print ds.int4 = 7.3dl" " = 7" Index: src-git/gdb/valarith.c =================================================================== --- src-git.orig/gdb/valarith.c 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/valarith.c 2007-12-26 18:59:27.000000000 -0200 @@ -28,6 +28,7 @@ #include "language.h" #include "gdb_string.h" #include "doublest.h" +#include "dfp.h" #include #include "infcall.h" @@ -741,6 +742,60 @@ value_concat (struct value *arg1, struct } +/* Obtain decimal value of arguments for binary operation, converting from + other types if one of them is not decimal floating point. */ +static void +value_args_as_decimal (struct value *arg1, struct value *arg2, + gdb_byte *x, int *len_x, gdb_byte *y, int *len_y) +{ + struct type *type1, *type2; + + type1 = check_typedef (value_type (arg1)); + type2 = check_typedef (value_type (arg2)); + + /* At least one of the arguments must be of decimal float type. */ + gdb_assert (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT + || TYPE_CODE (type2) == TYPE_CODE_DECFLOAT); + + if (TYPE_CODE (type1) == TYPE_CODE_FLT + || TYPE_CODE (type2) == TYPE_CODE_FLT) + /* The DFP extension to the C language does not allow mixing of + * decimal float types with other float types in expressions + * (see WDTR 24732, page 12). */ + error (_("Mixing decimal floating types with other floating types is not allowed.")); + + /* Obtain decimal value of arg1, converting from other types + if necessary. */ + + if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT) + { + *len_x = TYPE_LENGTH (type1); + memcpy (x, value_contents (arg1), *len_x); + } + else if (is_integral_type (type1)) + { + *len_x = TYPE_LENGTH (type2); + decimal_from_integral (arg1, x, *len_x); + } + else + error (_("Don't know how to convert to decimal floating type.")); + + /* Obtain decimal value of arg2, converting from other types + if necessary. */ + + if (TYPE_CODE (type2) == TYPE_CODE_DECFLOAT) + { + *len_y = TYPE_LENGTH (type2); + memcpy (y, value_contents (arg2), *len_y); + } + else if (is_integral_type (type2)) + { + *len_y = TYPE_LENGTH (type1); + decimal_from_integral (arg2, y, *len_y); + } + else + error (_("Don't know how to convert to decimal floating type.")); +} /* Perform a binary operation on two operands which have reasonable representations as integers or floats. This includes booleans, @@ -759,14 +814,55 @@ value_binop (struct value *arg1, struct type1 = check_typedef (value_type (arg1)); type2 = check_typedef (value_type (arg2)); - if ((TYPE_CODE (type1) != TYPE_CODE_FLT && !is_integral_type (type1)) + if ((TYPE_CODE (type1) != TYPE_CODE_FLT + && TYPE_CODE (type1) != TYPE_CODE_DECFLOAT && !is_integral_type (type1)) || - (TYPE_CODE (type2) != TYPE_CODE_FLT && !is_integral_type (type2))) + (TYPE_CODE (type2) != TYPE_CODE_FLT + && TYPE_CODE (type2) != TYPE_CODE_DECFLOAT && !is_integral_type (type2))) error (_("Argument to arithmetic operation not a number or boolean.")); - if (TYPE_CODE (type1) == TYPE_CODE_FLT + if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT || - TYPE_CODE (type2) == TYPE_CODE_FLT) + TYPE_CODE (type2) == TYPE_CODE_DECFLOAT) + { + struct type *v_type; + int len_v1, len_v2, len_v; + gdb_byte v1[16], v2[16]; + gdb_byte v[16]; + + value_args_as_decimal (arg1, arg2, v1, &len_v1, v2, &len_v2); + + switch (op) + { + case BINOP_ADD: + case BINOP_SUB: + case BINOP_MUL: + case BINOP_DIV: + case BINOP_EXP: + decimal_binop (op, v1, len_v1, v2, len_v2, v, &len_v); + break; + + default: + error (_("Integer-only operation on floating point number.")); + } + + if (TYPE_CODE (type1) != TYPE_CODE_DECFLOAT) + /* If arg1 is not a decimal float, the type of the result is the type + of the decimal float argument, arg2. */ + v_type = type2; + else if (TYPE_CODE (type2) != TYPE_CODE_DECFLOAT) + /* Same logic, for the case where arg2 is not a decimal float. */ + v_type = type1; + else + /* len_v is equal either to len_v1 or to len_v2. the type of the + result is the type of the argument with the same length as v. */ + v_type = (len_v == len_v1)? type1 : type2; + + val = value_from_decfloat (v_type, v); + } + else if (TYPE_CODE (type1) == TYPE_CODE_FLT + || + TYPE_CODE (type2) == TYPE_CODE_FLT) { /* FIXME-if-picky-about-floating-accuracy: Should be doing this in target format. real.c in GCC probably has the necessary @@ -1177,6 +1273,8 @@ value_logical_not (struct value *arg1) if (TYPE_CODE (type1) == TYPE_CODE_FLT) return 0 == value_as_double (arg1); + else if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT) + return decimal_is_zero (value_contents (arg1), TYPE_LENGTH (type1)); len = TYPE_LENGTH (type1); p = value_contents (arg1); @@ -1255,6 +1353,16 @@ value_equal (struct value *arg1, struct DOUBLEST d = value_as_double (arg1); return d == value_as_double (arg2); } + else if ((code1 == TYPE_CODE_DECFLOAT || is_int1) + && (code2 == TYPE_CODE_DECFLOAT || is_int2)) + { + gdb_byte v1[16], v2[16]; + int len_v1, len_v2; + + value_args_as_decimal (arg1, arg2, v1, &len_v1, v2, &len_v2); + + return decimal_compare (v1, len_v1, v2, len_v2) == 0; + } /* FIXME: Need to promote to either CORE_ADDR or LONGEST, whichever is bigger. */ @@ -1319,6 +1427,16 @@ value_less (struct value *arg1, struct v DOUBLEST d = value_as_double (arg1); return d < value_as_double (arg2); } + else if ((code1 == TYPE_CODE_DECFLOAT || is_int1) + && (code2 == TYPE_CODE_DECFLOAT || is_int2)) + { + gdb_byte v1[16], v2[16]; + int len_v1, len_v2; + + value_args_as_decimal (arg1, arg2, v1, &len_v1, v2, &len_v2); + + return decimal_compare (v1, len_v1, v2, len_v2) == -1; + } else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR) return value_as_address (arg1) < value_as_address (arg2); @@ -1350,6 +1468,8 @@ value_pos (struct value *arg1) if (TYPE_CODE (type) == TYPE_CODE_FLT) return value_from_double (type, value_as_double (arg1)); + else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) + return value_from_decfloat (type, value_contents (arg1)); else if (is_integral_type (type)) { /* Perform integral promotion for ANSI C/C++. FIXME: What about @@ -1382,7 +1502,7 @@ value_neg (struct value *arg1) int len = TYPE_LENGTH (type); gdb_byte decbytes[16]; /* a decfloat is at most 128 bits long */ - memcpy(decbytes, value_contents(arg1), len); + memcpy (decbytes, value_contents (arg1), len); if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_LITTLE) decbytes[len-1] = decbytes[len - 1] | 0x80; Index: src-git/gdb/valops.c =================================================================== --- src-git.orig/gdb/valops.c 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/valops.c 2007-12-26 18:59:27.000000000 -0200 @@ -36,6 +36,7 @@ #include "infcall.h" #include "dictionary.h" #include "cp-support.h" +#include "dfp.h" #include #include "gdb_string.h" @@ -338,7 +339,8 @@ value_cast (struct type *type, struct va code2 = TYPE_CODE_INT; scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT - || code2 == TYPE_CODE_ENUM || code2 == TYPE_CODE_RANGE); + || code2 == TYPE_CODE_DECFLOAT || code2 == TYPE_CODE_ENUM + || code2 == TYPE_CODE_RANGE); if (code1 == TYPE_CODE_STRUCT && code2 == TYPE_CODE_STRUCT @@ -357,6 +359,22 @@ value_cast (struct type *type, struct va } if (code1 == TYPE_CODE_FLT && scalar) return value_from_double (type, value_as_double (arg2)); + else if (code1 == TYPE_CODE_DECFLOAT && scalar) + { + int dec_len = TYPE_LENGTH (type); + gdb_byte dec[16]; + + if (code2 == TYPE_CODE_FLT) + decimal_from_floating (arg2, dec, dec_len); + else if (code2 == TYPE_CODE_DECFLOAT) + decimal_convert (value_contents (arg2), TYPE_LENGTH (type2), + dec, dec_len); + else + /* The only option left is an integral type. */ + decimal_from_integral (arg2, dec, dec_len); + + return value_from_decfloat (type, dec); + } else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM || code1 == TYPE_CODE_RANGE) && (scalar || code2 == TYPE_CODE_PTR Index: src-git/gdb/value.c =================================================================== --- src-git.orig/gdb/value.c 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/value.c 2007-12-26 18:59:27.000000000 -0200 @@ -982,6 +982,7 @@ value_as_double (struct value *val) error (_("Invalid floating value found in program.")); return foo; } + /* Extract a value as a C pointer. Does not deallocate the value. Note that val's type may not actually be a pointer; value_as_long handles all the cases. */ @@ -1127,6 +1128,11 @@ unpack_long (struct type *type, const gd case TYPE_CODE_FLT: return extract_typed_floating (valaddr, type); + case TYPE_CODE_DECFLOAT: + /* libdecnumber has a function to convert from decimal to integer, but + it doesn't work when the decimal number has a fractional part. */ + return decimal_to_double (valaddr, len); + case TYPE_CODE_PTR: case TYPE_CODE_REF: /* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure @@ -1184,6 +1190,8 @@ unpack_double (struct type *type, const return extract_typed_floating (valaddr, type); } + else if (code == TYPE_CODE_DECFLOAT) + return decimal_to_double (valaddr, len); else if (nosign) { /* Unsigned -- be sure we compensate for signed LONGEST. */ @@ -1643,23 +1651,12 @@ value_from_double (struct type *type, DO } struct value * -value_from_decfloat (struct type *expect_type, struct type *type, - gdb_byte decbytes[16]) +value_from_decfloat (struct type *type, const gdb_byte *dec) { struct value *val = allocate_value (type); - int len = TYPE_LENGTH (type); - if (expect_type) - { - int expect_len = TYPE_LENGTH (expect_type); - char decstr[MAX_DECIMAL_STRING]; - int real_len; - - decimal_to_string (decbytes, len, decstr); - decimal_from_string (decbytes, expect_len, decstr); - } + memcpy (value_contents_raw (val), dec, TYPE_LENGTH (type)); - memcpy (value_contents_raw (val), decbytes, len); return val; } Index: src-git/gdb/value.h =================================================================== --- src-git.orig/gdb/value.h 2007-12-26 18:59:12.000000000 -0200 +++ src-git/gdb/value.h 2007-12-26 18:59:27.000000000 -0200 @@ -280,9 +280,8 @@ extern void pack_long (gdb_byte *buf, st extern struct value *value_from_longest (struct type *type, LONGEST num); extern struct value *value_from_pointer (struct type *type, CORE_ADDR addr); extern struct value *value_from_double (struct type *type, DOUBLEST num); -extern struct value *value_from_decfloat (struct type *expect_type, - struct type *type, - gdb_byte decbytes[16]); +extern struct value *value_from_decfloat (struct type *type, + const gdb_byte *decbytes); extern struct value *value_from_string (char *string); extern struct value *value_at (struct type *type, CORE_ADDR addr); Index: src-git/gdb/doc/gdb.texinfo =================================================================== --- src-git.orig/gdb/doc/gdb.texinfo 2007-12-28 01:11:37.000000000 -0200 +++ src-git/gdb/doc/gdb.texinfo 2008-01-02 14:14:48.000000000 -0200 @@ -9139,6 +9139,7 @@ gcc.info, Using the @sc{gnu} Compiler Co * C Checks:: C and C@t{++} type and range checks * Debugging C:: @value{GDBN} and C * Debugging C Plus Plus:: @value{GDBN} features for C@t{++} +* Decimal Floating Point:: Numbers in Decimal Floating Point format @end menu @node C Operators @@ -9607,6 +9608,28 @@ available choices, or to finish the type @xref{Completion,, Command Completion}, for details on how to do this. @end table +@node Decimal Floating Point +@subsubsection Decimal Floating Point format +@cindex decimal floating point format + +@value{GDBN} can examine, set and perform computations with numbers in +decimal floating point format, which in the C language correspond to the +@code{_Decimal32}, @code{_Decimal64} and @code{_Decimal128} types as +specified by the extension to support decimal floating-point arithmetic. + +There are two encodings in use, depending on the architecture: BID (Binary +Integer Decimal) for x86 and x86-64, and DPD (Densely Packed Decimal) for +PowerPC. @value{GDBN} will use the appropriate encoding for the configured +target. + +Because of a limitation in @file{libdecnumber}, the library used by @value{GDBN} +to manipulate decimal floating point numbers, it is not possible to convert +(using a cast, for example) integers wider than 32-bit to decimal float. + +In addition, in order to imitate @value{GDBN}'s behaviour with binary floating +point computations, error checking in decimal float operations ignores +underflow, overflow and divide by zero exceptions. + @node Objective-C @subsection Objective-C --=-2jqMC27Pg/EuOfxJsnTh--