diff options
Diffstat (limited to 'gcc/dfp.c')
-rw-r--r-- | gcc/dfp.c | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/gcc/dfp.c b/gcc/dfp.c new file mode 100644 index 000000000..5a18db9f5 --- /dev/null +++ b/gcc/dfp.c @@ -0,0 +1,719 @@ +/* Decimal floating point support. + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software + Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tm_p.h" +#include "dfp.h" + +/* The order of the following headers is important for making sure + decNumber structure is large enough to hold decimal128 digits. */ + +#include "decimal128.h" +#include "decimal128Local.h" +#include "decimal64.h" +#include "decimal32.h" +#include "decNumber.h" + +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN 0 +#endif + +/* Initialize R (a real with the decimal flag set) from DN. Can + utilize status passed in via CONTEXT, if a previous operation had + interesting status. */ + +static void +decimal_from_decnumber (REAL_VALUE_TYPE *r, decNumber *dn, decContext *context) +{ + memset (r, 0, sizeof (REAL_VALUE_TYPE)); + + r->cl = rvc_normal; + if (decNumberIsNaN (dn)) + r->cl = rvc_nan; + if (decNumberIsInfinite (dn)) + r->cl = rvc_inf; + if (context->status & DEC_Overflow) + r->cl = rvc_inf; + if (decNumberIsNegative (dn)) + r->sign = 1; + r->decimal = 1; + + if (r->cl != rvc_normal) + return; + + decContextDefault (context, DEC_INIT_DECIMAL128); + context->traps = 0; + + decimal128FromNumber ((decimal128 *) r->sig, dn, context); +} + +/* Create decimal encoded R from string S. */ + +void +decimal_real_from_string (REAL_VALUE_TYPE *r, const char *s) +{ + decNumber dn; + decContext set; + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + decNumberFromString (&dn, s, &set); + + /* It would be more efficient to store directly in decNumber format, + but that is impractical from current data structure size. + Encoding as a decimal128 is much more compact. */ + decimal_from_decnumber (r, &dn, &set); +} + +/* Initialize a decNumber from a REAL_VALUE_TYPE. */ + +static void +decimal_to_decnumber (const REAL_VALUE_TYPE *r, decNumber *dn) +{ + decContext set; + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + switch (r->cl) + { + case rvc_zero: + decNumberZero (dn); + break; + case rvc_inf: + decNumberFromString (dn, "Infinity", &set); + break; + case rvc_nan: + if (r->signalling) + decNumberFromString (dn, "snan", &set); + else + decNumberFromString (dn, "nan", &set); + break; + case rvc_normal: + gcc_assert (r->decimal); + decimal128ToNumber ((const decimal128 *) r->sig, dn); + break; + default: + gcc_unreachable (); + } + + /* Fix up sign bit. */ + if (r->sign != decNumberIsNegative (dn)) + dn->bits ^= DECNEG; +} + +/* Encode a real into an IEEE 754 decimal32 type. */ + +void +encode_decimal32 (const struct real_format *fmt ATTRIBUTE_UNUSED, + long *buf, const REAL_VALUE_TYPE *r) +{ + decNumber dn; + decimal32 d32; + decContext set; + int32_t image; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + decimal_to_decnumber (r, &dn); + decimal32FromNumber (&d32, &dn, &set); + + memcpy (&image, d32.bytes, sizeof (int32_t)); + buf[0] = image; +} + +/* Decode an IEEE 754 decimal32 type into a real. */ + +void +decode_decimal32 (const struct real_format *fmt ATTRIBUTE_UNUSED, + REAL_VALUE_TYPE *r, const long *buf) +{ + decNumber dn; + decimal32 d32; + decContext set; + int32_t image; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + image = buf[0]; + memcpy (&d32.bytes, &image, sizeof (int32_t)); + + decimal32ToNumber (&d32, &dn); + decimal_from_decnumber (r, &dn, &set); +} + +/* Encode a real into an IEEE 754 decimal64 type. */ + +void +encode_decimal64 (const struct real_format *fmt ATTRIBUTE_UNUSED, + long *buf, const REAL_VALUE_TYPE *r) +{ + decNumber dn; + decimal64 d64; + decContext set; + int32_t image; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + decimal_to_decnumber (r, &dn); + decimal64FromNumber (&d64, &dn, &set); + + if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN) + { + memcpy (&image, &d64.bytes[0], sizeof (int32_t)); + buf[0] = image; + memcpy (&image, &d64.bytes[4], sizeof (int32_t)); + buf[1] = image; + } + else + { + memcpy (&image, &d64.bytes[4], sizeof (int32_t)); + buf[0] = image; + memcpy (&image, &d64.bytes[0], sizeof (int32_t)); + buf[1] = image; + } +} + +/* Decode an IEEE 754 decimal64 type into a real. */ + +void +decode_decimal64 (const struct real_format *fmt ATTRIBUTE_UNUSED, + REAL_VALUE_TYPE *r, const long *buf) +{ + decNumber dn; + decimal64 d64; + decContext set; + int32_t image; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN) + { + image = buf[0]; + memcpy (&d64.bytes[0], &image, sizeof (int32_t)); + image = buf[1]; + memcpy (&d64.bytes[4], &image, sizeof (int32_t)); + } + else + { + image = buf[1]; + memcpy (&d64.bytes[0], &image, sizeof (int32_t)); + image = buf[0]; + memcpy (&d64.bytes[4], &image, sizeof (int32_t)); + } + + decimal64ToNumber (&d64, &dn); + decimal_from_decnumber (r, &dn, &set); +} + +/* Encode a real into an IEEE 754 decimal128 type. */ + +void +encode_decimal128 (const struct real_format *fmt ATTRIBUTE_UNUSED, + long *buf, const REAL_VALUE_TYPE *r) +{ + decNumber dn; + decContext set; + decimal128 d128; + int32_t image; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + decimal_to_decnumber (r, &dn); + decimal128FromNumber (&d128, &dn, &set); + + if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN) + { + memcpy (&image, &d128.bytes[0], sizeof (int32_t)); + buf[0] = image; + memcpy (&image, &d128.bytes[4], sizeof (int32_t)); + buf[1] = image; + memcpy (&image, &d128.bytes[8], sizeof (int32_t)); + buf[2] = image; + memcpy (&image, &d128.bytes[12], sizeof (int32_t)); + buf[3] = image; + } + else + { + memcpy (&image, &d128.bytes[12], sizeof (int32_t)); + buf[0] = image; + memcpy (&image, &d128.bytes[8], sizeof (int32_t)); + buf[1] = image; + memcpy (&image, &d128.bytes[4], sizeof (int32_t)); + buf[2] = image; + memcpy (&image, &d128.bytes[0], sizeof (int32_t)); + buf[3] = image; + } +} + +/* Decode an IEEE 754 decimal128 type into a real. */ + +void +decode_decimal128 (const struct real_format *fmt ATTRIBUTE_UNUSED, + REAL_VALUE_TYPE *r, const long *buf) +{ + decNumber dn; + decimal128 d128; + decContext set; + int32_t image; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN) + { + image = buf[0]; + memcpy (&d128.bytes[0], &image, sizeof (int32_t)); + image = buf[1]; + memcpy (&d128.bytes[4], &image, sizeof (int32_t)); + image = buf[2]; + memcpy (&d128.bytes[8], &image, sizeof (int32_t)); + image = buf[3]; + memcpy (&d128.bytes[12], &image, sizeof (int32_t)); + } + else + { + image = buf[3]; + memcpy (&d128.bytes[0], &image, sizeof (int32_t)); + image = buf[2]; + memcpy (&d128.bytes[4], &image, sizeof (int32_t)); + image = buf[1]; + memcpy (&d128.bytes[8], &image, sizeof (int32_t)); + image = buf[0]; + memcpy (&d128.bytes[12], &image, sizeof (int32_t)); + } + + decimal128ToNumber (&d128, &dn); + decimal_from_decnumber (r, &dn, &set); +} + +/* Helper function to convert from a binary real internal + representation. */ + +static void +decimal_to_binary (REAL_VALUE_TYPE *to, const REAL_VALUE_TYPE *from, + enum machine_mode mode) +{ + char string[256]; + const decimal128 *const d128 = (const decimal128 *) from->sig; + + decimal128ToString (d128, string); + real_from_string3 (to, string, mode); +} + + +/* Helper function to convert from a binary real internal + representation. */ + +static void +decimal_from_binary (REAL_VALUE_TYPE *to, const REAL_VALUE_TYPE *from) +{ + char string[256]; + + /* We convert to string, then to decNumber then to decimal128. */ + real_to_decimal (string, from, sizeof (string), 0, 1); + decimal_real_from_string (to, string); +} + +/* Helper function to real.c:do_compare() to handle decimal internal + representation including when one of the operands is still in the + binary internal representation. */ + +int +decimal_do_compare (const REAL_VALUE_TYPE *a, const REAL_VALUE_TYPE *b, + int nan_result) +{ + decContext set; + decNumber dn, dn2, dn3; + REAL_VALUE_TYPE a1, b1; + + /* If either operand is non-decimal, create temporary versions. */ + if (!a->decimal) + { + decimal_from_binary (&a1, a); + a = &a1; + } + if (!b->decimal) + { + decimal_from_binary (&b1, b); + b = &b1; + } + + /* Convert into decNumber form for comparison operation. */ + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + decimal128ToNumber ((const decimal128 *) a->sig, &dn2); + decimal128ToNumber ((const decimal128 *) b->sig, &dn3); + + /* Finally, do the comparison. */ + decNumberCompare (&dn, &dn2, &dn3, &set); + + /* Return the comparison result. */ + if (decNumberIsNaN (&dn)) + return nan_result; + else if (decNumberIsZero (&dn)) + return 0; + else if (decNumberIsNegative (&dn)) + return -1; + else + return 1; +} + +/* Helper to round_for_format, handling decimal float types. */ + +void +decimal_round_for_format (const struct real_format *fmt, REAL_VALUE_TYPE *r) +{ + decNumber dn; + decContext set; + + /* Real encoding occurs later. */ + if (r->cl != rvc_normal) + return; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + decimal128ToNumber ((decimal128 *) r->sig, &dn); + + if (fmt == &decimal_quad_format) + { + /* The internal format is already in this format. */ + return; + } + else if (fmt == &decimal_single_format) + { + decimal32 d32; + decContextDefault (&set, DEC_INIT_DECIMAL32); + set.traps = 0; + + decimal32FromNumber (&d32, &dn, &set); + decimal32ToNumber (&d32, &dn); + } + else if (fmt == &decimal_double_format) + { + decimal64 d64; + decContextDefault (&set, DEC_INIT_DECIMAL64); + set.traps = 0; + + decimal64FromNumber (&d64, &dn, &set); + decimal64ToNumber (&d64, &dn); + } + else + gcc_unreachable (); + + decimal_from_decnumber (r, &dn, &set); +} + +/* Extend or truncate to a new mode. Handles conversions between + binary and decimal types. */ + +void +decimal_real_convert (REAL_VALUE_TYPE *r, enum machine_mode mode, + const REAL_VALUE_TYPE *a) +{ + const struct real_format *fmt = REAL_MODE_FORMAT (mode); + + if (a->decimal && fmt->b == 10) + return; + if (a->decimal) + decimal_to_binary (r, a, mode); + else + decimal_from_binary (r, a); +} + +/* Render R_ORIG as a decimal floating point constant. Emit DIGITS + significant digits in the result, bounded by BUF_SIZE. If DIGITS + is 0, choose the maximum for the representation. If + CROP_TRAILING_ZEROS, strip trailing zeros. Currently, not honoring + DIGITS or CROP_TRAILING_ZEROS. */ + +void +decimal_real_to_decimal (char *str, const REAL_VALUE_TYPE *r_orig, + size_t buf_size, + size_t digits ATTRIBUTE_UNUSED, + int crop_trailing_zeros ATTRIBUTE_UNUSED) +{ + const decimal128 *const d128 = (const decimal128*) r_orig->sig; + + /* decimal128ToString requires space for at least 24 characters; + Require two more for suffix. */ + gcc_assert (buf_size >= 24); + decimal128ToString (d128, str); +} + +static bool +decimal_do_add (REAL_VALUE_TYPE *r, const REAL_VALUE_TYPE *op0, + const REAL_VALUE_TYPE *op1, int subtract_p) +{ + decNumber dn; + decContext set; + decNumber dn2, dn3; + + decimal_to_decnumber (op0, &dn2); + decimal_to_decnumber (op1, &dn3); + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + if (subtract_p) + decNumberSubtract (&dn, &dn2, &dn3, &set); + else + decNumberAdd (&dn, &dn2, &dn3, &set); + + decimal_from_decnumber (r, &dn, &set); + + /* Return true, if inexact. */ + return (set.status & DEC_Inexact); +} + +/* Compute R = OP0 * OP1. */ + +static bool +decimal_do_multiply (REAL_VALUE_TYPE *r, const REAL_VALUE_TYPE *op0, + const REAL_VALUE_TYPE *op1) +{ + decContext set; + decNumber dn, dn2, dn3; + + decimal_to_decnumber (op0, &dn2); + decimal_to_decnumber (op1, &dn3); + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + decNumberMultiply (&dn, &dn2, &dn3, &set); + decimal_from_decnumber (r, &dn, &set); + + /* Return true, if inexact. */ + return (set.status & DEC_Inexact); +} + +/* Compute R = OP0 / OP1. */ + +static bool +decimal_do_divide (REAL_VALUE_TYPE *r, const REAL_VALUE_TYPE *op0, + const REAL_VALUE_TYPE *op1) +{ + decContext set; + decNumber dn, dn2, dn3; + + decimal_to_decnumber (op0, &dn2); + decimal_to_decnumber (op1, &dn3); + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + + decNumberDivide (&dn, &dn2, &dn3, &set); + decimal_from_decnumber (r, &dn, &set); + + /* Return true, if inexact. */ + return (set.status & DEC_Inexact); +} + +/* Set R to A truncated to an integral value toward zero (decimal + floating point). */ + +void +decimal_do_fix_trunc (REAL_VALUE_TYPE *r, const REAL_VALUE_TYPE *a) +{ + decNumber dn, dn2; + decContext set; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + set.round = DEC_ROUND_DOWN; + decimal128ToNumber ((const decimal128 *) a->sig, &dn2); + + decNumberToIntegralValue (&dn, &dn2, &set); + decimal_from_decnumber (r, &dn, &set); +} + +/* Render decimal float value R as an integer. */ + +HOST_WIDE_INT +decimal_real_to_integer (const REAL_VALUE_TYPE *r) +{ + decContext set; + decNumber dn, dn2, dn3; + REAL_VALUE_TYPE to; + char string[256]; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + set.round = DEC_ROUND_DOWN; + decimal128ToNumber ((const decimal128 *) r->sig, &dn); + + decNumberToIntegralValue (&dn2, &dn, &set); + decNumberZero (&dn3); + decNumberRescale (&dn, &dn2, &dn3, &set); + + /* Convert to REAL_VALUE_TYPE and call appropriate conversion + function. */ + decNumberToString (&dn, string); + real_from_string (&to, string); + return real_to_integer (&to); +} + +/* Likewise, but to an integer pair, HI+LOW. */ + +void +decimal_real_to_integer2 (HOST_WIDE_INT *plow, HOST_WIDE_INT *phigh, + const REAL_VALUE_TYPE *r) +{ + decContext set; + decNumber dn, dn2, dn3; + REAL_VALUE_TYPE to; + char string[256]; + + decContextDefault (&set, DEC_INIT_DECIMAL128); + set.traps = 0; + set.round = DEC_ROUND_DOWN; + decimal128ToNumber ((const decimal128 *) r->sig, &dn); + + decNumberToIntegralValue (&dn2, &dn, &set); + decNumberZero (&dn3); + decNumberRescale (&dn, &dn2, &dn3, &set); + + /* Convert to REAL_VALUE_TYPE and call appropriate conversion + function. */ + decNumberToString (&dn, string); + real_from_string (&to, string); + real_to_integer2 (plow, phigh, &to); +} + +/* Perform the decimal floating point operation described by CODE. + For a unary operation, OP1 will be NULL. This function returns + true if the result may be inexact due to loss of precision. */ + +bool +decimal_real_arithmetic (REAL_VALUE_TYPE *r, enum tree_code code, + const REAL_VALUE_TYPE *op0, + const REAL_VALUE_TYPE *op1) +{ + REAL_VALUE_TYPE a, b; + + /* If either operand is non-decimal, create temporaries. */ + if (!op0->decimal) + { + decimal_from_binary (&a, op0); + op0 = &a; + } + if (op1 && !op1->decimal) + { + decimal_from_binary (&b, op1); + op1 = &b; + } + + switch (code) + { + case PLUS_EXPR: + return decimal_do_add (r, op0, op1, 0); + + case MINUS_EXPR: + return decimal_do_add (r, op0, op1, 1); + + case MULT_EXPR: + return decimal_do_multiply (r, op0, op1); + + case RDIV_EXPR: + return decimal_do_divide (r, op0, op1); + + case MIN_EXPR: + if (op1->cl == rvc_nan) + *r = *op1; + else if (real_compare (UNLT_EXPR, op0, op1)) + *r = *op0; + else + *r = *op1; + return false; + + case MAX_EXPR: + if (op1->cl == rvc_nan) + *r = *op1; + else if (real_compare (LT_EXPR, op0, op1)) + *r = *op1; + else + *r = *op0; + return false; + + case NEGATE_EXPR: + { + *r = *op0; + /* Flip sign bit. */ + decimal128FlipSign ((decimal128 *) r->sig); + /* Keep sign field in sync. */ + r->sign ^= 1; + } + return false; + + case ABS_EXPR: + { + *r = *op0; + /* Clear sign bit. */ + decimal128ClearSign ((decimal128 *) r->sig); + /* Keep sign field in sync. */ + r->sign = 0; + } + return false; + + case FIX_TRUNC_EXPR: + decimal_do_fix_trunc (r, op0); + return false; + + default: + gcc_unreachable (); + } +} + +/* Fills R with the largest finite value representable in mode MODE. + If SIGN is nonzero, R is set to the most negative finite value. */ + +void +decimal_real_maxval (REAL_VALUE_TYPE *r, int sign, enum machine_mode mode) +{ + const char *max; + + switch (mode) + { + case SDmode: + max = "9.999999E96"; + break; + case DDmode: + max = "9.999999999999999E384"; + break; + case TDmode: + max = "9.999999999999999999999999999999999E6144"; + break; + default: + gcc_unreachable (); + } + + decimal_real_from_string (r, max); + if (sign) + decimal128SetSign ((decimal128 *) r->sig, 1); +} |