diff options
author | Jakub Jelinek <jakub@redhat.com> | 2018-05-04 09:19:45 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2018-05-04 09:19:45 +0200 |
commit | 047823853d8324eab7d6ad8f266ee5395c4a76ff (patch) | |
tree | 577de47e3f2f5746febab4c2403441ef0d8abd9f /gcc/real.c | |
parent | 105073e1cc39fbeb03aa40e294ffc3c400cfa844 (diff) | |
download | gcc-047823853d8324eab7d6ad8f266ee5395c4a76ff.zip gcc-047823853d8324eab7d6ad8f266ee5395c4a76ff.tar.gz gcc-047823853d8324eab7d6ad8f266ee5395c4a76ff.tar.bz2 |
re PR tree-optimization/85466 (Performance is slow when doing 'branchless' conditional style math operations)
PR libstdc++/85466
* real.h (real_nextafter): Declare.
* real.c (real_nextafter): New function.
* fold-const-call.c (fold_const_nextafter): New function.
(fold_const_call_sss): Call it for CASE_CFN_NEXTAFTER and
CASE_CFN_NEXTTOWARD.
(fold_const_call_1): For CASE_CFN_NEXTTOWARD call fold_const_call_sss
even when arg1_mode is different from arg0_mode.
* gcc.dg/nextafter-1.c: New test.
* gcc.dg/nextafter-2.c: New test.
* gcc.dg/nextafter-3.c: New test.
* gcc.dg/nextafter-4.c: New test.
From-SVN: r259921
Diffstat (limited to 'gcc/real.c')
-rw-r--r-- | gcc/real.c | 96 |
1 files changed, 96 insertions, 0 deletions
@@ -5048,6 +5048,102 @@ real_isinteger (const REAL_VALUE_TYPE *c, HOST_WIDE_INT *int_out) return false; } +/* Calculate nextafter (X, Y) or nexttoward (X, Y). Return true if + underflow or overflow needs to be raised. */ + +bool +real_nextafter (REAL_VALUE_TYPE *r, format_helper fmt, + const REAL_VALUE_TYPE *x, const REAL_VALUE_TYPE *y) +{ + int cmp = do_compare (x, y, 2); + /* If either operand is NaN, return qNaN. */ + if (cmp == 2) + { + get_canonical_qnan (r, 0); + return false; + } + /* If x == y, return y cast to target type. */ + if (cmp == 0) + { + real_convert (r, fmt, y); + return false; + } + + if (x->cl == rvc_zero) + { + get_zero (r, y->sign); + r->cl = rvc_normal; + SET_REAL_EXP (r, fmt->emin - fmt->p + 1); + r->sig[SIGSZ - 1] = SIG_MSB; + return false; + } + + int np2 = SIGNIFICAND_BITS - fmt->p; + /* For denormals adjust np2 correspondingly. */ + if (x->cl == rvc_normal && REAL_EXP (x) < fmt->emin) + np2 += fmt->emin - REAL_EXP (x); + + REAL_VALUE_TYPE u; + get_zero (r, x->sign); + get_zero (&u, 0); + set_significand_bit (&u, np2); + r->cl = rvc_normal; + SET_REAL_EXP (r, REAL_EXP (x)); + + if (x->cl == rvc_inf) + { + bool borrow = sub_significands (r, r, &u, 0); + gcc_assert (borrow); + SET_REAL_EXP (r, fmt->emax); + } + else if (cmp == (x->sign ? 1 : -1)) + { + if (add_significands (r, x, &u)) + { + /* Overflow. Means the significand had been all ones, and + is now all zeros. Need to increase the exponent, and + possibly re-normalize it. */ + SET_REAL_EXP (r, REAL_EXP (r) + 1); + if (REAL_EXP (r) > fmt->emax) + { + get_inf (r, x->sign); + return true; + } + r->sig[SIGSZ - 1] = SIG_MSB; + } + } + else + { + if (REAL_EXP (x) > fmt->emin && x->sig[SIGSZ - 1] == SIG_MSB) + { + int i; + for (i = SIGSZ - 2; i >= 0; i--) + if (x->sig[i]) + break; + if (i < 0) + { + /* When mantissa is 1.0, we need to subtract only + half of u: nextafter (1.0, 0.0) is 1.0 - __DBL_EPSILON__ / 2 + rather than 1.0 - __DBL_EPSILON__. */ + clear_significand_bit (&u, np2); + np2--; + set_significand_bit (&u, np2); + } + } + sub_significands (r, x, &u, 0); + } + + /* Clear out trailing garbage. */ + clear_significand_below (r, np2); + normalize (r); + if (REAL_EXP (r) <= fmt->emin - fmt->p) + { + get_zero (r, x->sign); + return true; + } + return r->cl == rvc_zero; +} + /* Write into BUF the maximum representable finite floating-point number, (1 - b**-p) * b**emax for a given FP format FMT as a hex float string. LEN is the size of BUF, and the buffer must be large |