/* Decimal floating point support. Copyright (C) 2005-2014 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 . */ #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: if (!r->decimal) { /* dconst{1,2,m1,half} are used in various places in the middle-end and optimizers, allow them here as an exception by converting them to decimal. */ if (memcmp (r, &dconst1, sizeof (*r)) == 0) { decNumberFromString (dn, "1", &set); break; } if (memcmp (r, &dconst2, sizeof (*r)) == 0) { decNumberFromString (dn, "2", &set); break; } if (memcmp (r, &dconstm1, sizeof (*r)) == 0) { decNumberFromString (dn, "-1", &set); break; } if (memcmp (r, &dconsthalf, sizeof (*r)) == 0) { decNumberFromString (dn, "0.5", &set); break; } gcc_unreachable (); } 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); }