diff options
Diffstat (limited to 'gcc/real.c')
-rw-r--r-- | gcc/real.c | 181 |
1 files changed, 116 insertions, 65 deletions
diff --git a/gcc/real.c b/gcc/real.c index 5cf2525b90a..f8e8d7fdfd9 100644 --- a/gcc/real.c +++ b/gcc/real.c @@ -29,6 +29,7 @@ #include "realmpfr.h" #include "tm_p.h" #include "dfp.h" +#include "wide-int.h" /* The floating point model used internally is not exactly IEEE 754 compliant, and close to the description in the ISO C99 standard, @@ -1370,43 +1371,39 @@ real_to_integer (const REAL_VALUE_TYPE *r) } } -/* Likewise, but to an integer pair, HI+LOW. */ +/* Likewise, but producing a wide-int of PRECISION. If the value cannot + be represented in precision, *FAIL is set to TRUE. */ -void -real_to_integer2 (HOST_WIDE_INT *plow, HOST_WIDE_INT *phigh, - const REAL_VALUE_TYPE *r) +wide_int +real_to_integer (const REAL_VALUE_TYPE *r, bool *fail, int precision) { - REAL_VALUE_TYPE t; - unsigned HOST_WIDE_INT low; - HOST_WIDE_INT high; + typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION * 2) real_int; + HOST_WIDE_INT val[2 * MAX_BITSIZE_MODE_ANY_INT / HOST_BITS_PER_WIDE_INT]; int exp; + int words; + wide_int result; + real_int tmp; + int w; switch (r->cl) { case rvc_zero: underflow: - low = high = 0; - break; + return wi::zero (precision); case rvc_inf: case rvc_nan: overflow: - high = (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1); + *fail = true; + if (r->sign) - low = 0; + return wi::set_bit_in_zero (precision - 1, precision); else - { - high--; - low = -1; - } - break; + return ~wi::set_bit_in_zero (precision - 1, precision); case rvc_normal: if (r->decimal) - { - decimal_real_to_integer2 (plow, phigh, r); - return; - } + return decimal_real_to_integer (r, fail, precision); exp = REAL_EXP (r); if (exp <= 0) @@ -1415,42 +1412,51 @@ real_to_integer2 (HOST_WIDE_INT *plow, HOST_WIDE_INT *phigh, undefined, so it doesn't matter what we return, and some callers expect to be able to use this routine for both signed and unsigned conversions. */ - if (exp > HOST_BITS_PER_DOUBLE_INT) + if (exp > precision) goto overflow; - rshift_significand (&t, r, HOST_BITS_PER_DOUBLE_INT - exp); - if (HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_LONG) + words = (precision + HOST_BITS_PER_WIDE_INT - 1) / HOST_BITS_PER_WIDE_INT; + + for (unsigned int i = 0; i < ARRAY_SIZE (val); i++) + val[i] = 0; + +#if (HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_LONG) + for (int i = 0; i < words; i++) { - high = t.sig[SIGSZ-1]; - low = t.sig[SIGSZ-2]; + int j = SIGSZ - words + i; + val[i] = (j < 0) ? 0 : r->sig[j]; } - else +#else + gcc_assert (HOST_BITS_PER_WIDE_INT == 2*HOST_BITS_PER_LONG); + for (int i = 0; i < words; i++) { - gcc_assert (HOST_BITS_PER_WIDE_INT == 2*HOST_BITS_PER_LONG); - high = t.sig[SIGSZ-1]; - high = high << (HOST_BITS_PER_LONG - 1) << 1; - high |= t.sig[SIGSZ-2]; - - low = t.sig[SIGSZ-3]; - low = low << (HOST_BITS_PER_LONG - 1) << 1; - low |= t.sig[SIGSZ-4]; + int j = SIGSZ - (words * 2) + (i + 2) + 1; + if (j < 0) + val[i] = 0; + else + { + val[i] = r->sig[j]; + unsigned HOST_WIDE_INT v = val[i]; + v <<= HOST_BITS_PER_LONG; + val[i] = v; + val[i] |= r->sig[j - 1]; + } } +#endif + w = SIGSZ * HOST_BITS_PER_LONG + words * HOST_BITS_PER_WIDE_INT; + tmp = real_int::from_array + (val, (w + HOST_BITS_PER_WIDE_INT - 1) / HOST_BITS_PER_WIDE_INT, w); + tmp = wi::lrshift (tmp, (words * HOST_BITS_PER_WIDE_INT) - exp); + result = wide_int::from (tmp, precision, UNSIGNED); if (r->sign) - { - if (low == 0) - high = -high; - else - low = -low, high = ~high; - } - break; + return -result; + else + return result; default: gcc_unreachable (); } - - *plow = low; - *phigh = high; } /* A subroutine of real_to_decimal. Compute the quotient and remainder @@ -2113,43 +2119,88 @@ real_from_string3 (REAL_VALUE_TYPE *r, const char *s, enum machine_mode mode) real_convert (r, mode, r); } -/* Initialize R from the integer pair HIGH+LOW. */ +/* Initialize R from the wide_int VAL_IN. The MODE is not VOIDmode,*/ void real_from_integer (REAL_VALUE_TYPE *r, enum machine_mode mode, - unsigned HOST_WIDE_INT low, HOST_WIDE_INT high, - int unsigned_p) + const wide_int_ref &val_in, signop sgn) { - if (low == 0 && high == 0) + if (val_in == 0) get_zero (r, 0); else { + unsigned int len = val_in.get_precision (); + int i, j, e = 0; + int maxbitlen = MAX_BITSIZE_MODE_ANY_INT + HOST_BITS_PER_WIDE_INT; + const unsigned int realmax = (SIGNIFICAND_BITS / HOST_BITS_PER_WIDE_INT + * HOST_BITS_PER_WIDE_INT); + memset (r, 0, sizeof (*r)); r->cl = rvc_normal; - r->sign = high < 0 && !unsigned_p; - SET_REAL_EXP (r, HOST_BITS_PER_DOUBLE_INT); + r->sign = wi::neg_p (val_in, sgn); + + /* We have to ensure we can negate the largest negative number. */ + wide_int val = wide_int::from (val_in, maxbitlen, sgn); if (r->sign) + val = -val; + + /* Ensure a multiple of HOST_BITS_PER_WIDE_INT, ceiling, as elt + won't work with precisions that are not a multiple of + HOST_BITS_PER_WIDE_INT. */ + len += HOST_BITS_PER_WIDE_INT - 1; + + /* Ensure we can represent the largest negative number. */ + len += 1; + + len = len/HOST_BITS_PER_WIDE_INT * HOST_BITS_PER_WIDE_INT; + + /* Cap the size to the size allowed by real.h. */ + if (len > realmax) { - high = ~high; - if (low == 0) - high += 1; - else - low = -low; + HOST_WIDE_INT cnt_l_z; + cnt_l_z = wi::clz (val); + + if (maxbitlen - cnt_l_z > realmax) + { + e = maxbitlen - cnt_l_z - realmax; + + /* This value is too large, we must shift it right to + preserve all the bits we can, and then bump the + exponent up by that amount. */ + val = wi::lrshift (val, e); + } + len = realmax; } + /* Clear out top bits so elt will work with precisions that aren't + a multiple of HOST_BITS_PER_WIDE_INT. */ + val = wide_int::from (val, len, sgn); + len = len / HOST_BITS_PER_WIDE_INT; + + SET_REAL_EXP (r, len * HOST_BITS_PER_WIDE_INT + e); + + j = SIGSZ - 1; if (HOST_BITS_PER_LONG == HOST_BITS_PER_WIDE_INT) - { - r->sig[SIGSZ-1] = high; - r->sig[SIGSZ-2] = low; - } + for (i = len - 1; i >= 0; i--) + { + r->sig[j--] = val.elt (i); + if (j < 0) + break; + } else { gcc_assert (HOST_BITS_PER_LONG*2 == HOST_BITS_PER_WIDE_INT); - r->sig[SIGSZ-1] = high >> (HOST_BITS_PER_LONG - 1) >> 1; - r->sig[SIGSZ-2] = high; - r->sig[SIGSZ-3] = low >> (HOST_BITS_PER_LONG - 1) >> 1; - r->sig[SIGSZ-4] = low; + for (i = len - 1; i >= 0; i--) + { + HOST_WIDE_INT e = val.elt (i); + r->sig[j--] = e >> (HOST_BITS_PER_LONG - 1) >> 1; + if (j < 0) + break; + r->sig[j--] = e; + if (j < 0) + break; + } } normalize (r); @@ -2239,7 +2290,7 @@ ten_to_ptwo (int n) for (i = 0; i < n; ++i) t *= t; - real_from_integer (&tens[n], VOIDmode, t, 0, 1); + real_from_integer (&tens[n], VOIDmode, t, UNSIGNED); } else { @@ -2278,7 +2329,7 @@ real_digit (int n) gcc_assert (n <= 9); if (n > 0 && num[n].cl == rvc_zero) - real_from_integer (&num[n], VOIDmode, n, 0, 1); + real_from_integer (&num[n], VOIDmode, n, UNSIGNED); return &num[n]; } |