diff options
author | Tejas Joshi <tejasjoshi9673@gmail.com> | 2019-08-26 12:32:29 +0000 |
---|---|---|
committer | Martin Jambor <jamborm@gcc.gnu.org> | 2019-08-26 14:32:29 +0200 |
commit | 7d7b99f95bf2517caab5f9300090b471135b4fc0 (patch) | |
tree | c215f66b35c9f3508ceb670fbaeae05b7d223c3d /gcc/real.c | |
parent | 48a31a09839b12127ce7c40d7adc4bd5bf1d3407 (diff) | |
download | gcc-7d7b99f95bf2517caab5f9300090b471135b4fc0.zip gcc-7d7b99f95bf2517caab5f9300090b471135b4fc0.tar.gz gcc-7d7b99f95bf2517caab5f9300090b471135b4fc0.tar.bz2 |
Builtin function roundeven folding implementation
2019-08-26 Tejas Joshi <tejasjoshi9673@gmail.com>
* builtins.c (mathfn_built_in_2): Added CASE_MATHFN_FLOATN
for ROUNDEVEN.
* builtins.def: Added function definitions for roundeven function
variants.
* fold-const-call.c (fold_const_call_ss): Added case for roundeven
function call. Adjust condition for floor, ceil, trunc and round.
* fold-const.c (negate_mathfn_p): Added case for roundeven function.
(tree_call_nonnegative_warnv_p): Added case for roundeven function.
(integer_valued_real_call_p): Added case for roundeven function.
* real.c (is_even): New function. Returns true if real number is even,
otherwise returns false.
(is_halfway_below): New function. Returns true if real number is
halfway between two integers, else return false.
(real_roundeven): New function. Round real number to nearest integer,
rounding halfway cases towards even.
* real.h (real_value): Added descriptive comments. Added function
declaration for roundeven function.
* doc/extend.texi (Other Builtins): List roundeven variants among
functions which can be handled as builtins.
gcc/testsuite/ChangeLog:
2019-08-26 Tejas Joshi <tejasjoshi9673@gmail.com>
* gcc.dg/torture/builtin-round-roundeven.c: New test.
* gcc.dg/torture/builtin-round-roundevenf128.c: New test.
From-SVN: r274927
Diffstat (limited to 'gcc/real.c')
-rw-r--r-- | gcc/real.c | 95 |
1 files changed, 95 insertions, 0 deletions
@@ -5010,6 +5010,101 @@ real_round (REAL_VALUE_TYPE *r, format_helper fmt, real_convert (r, fmt, r); } +/* Return true including 0 if integer part of R is even, else return + false. The function is not valid for rvc_inf and rvc_nan classes. */ + +bool +is_even (REAL_VALUE_TYPE *r) +{ + gcc_assert (r->cl != rvc_inf); + gcc_assert (r->cl != rvc_nan); + + if (r->cl == rvc_zero) + return true; + + /* For (-1,1), number is even. */ + if (REAL_EXP (r) <= 0) + return true; + + /* Check lowest bit, if not set, return true. */ + else if (REAL_EXP (r) <= SIGNIFICAND_BITS) + { + unsigned int n = SIGNIFICAND_BITS - REAL_EXP (r); + int w = n / HOST_BITS_PER_LONG; + + unsigned long num = ((unsigned long)1 << (n % HOST_BITS_PER_LONG)); + + if ((r->sig[w] & num) == 0) + return true; + } + else + return true; + + return false; +} + +/* Return true if R is halfway between two integers, else return + false. The function is not valid for rvc_inf and rvc_nan classes. */ + +bool +is_halfway_below (const REAL_VALUE_TYPE *r) +{ + gcc_assert (r->cl != rvc_inf); + gcc_assert (r->cl != rvc_nan); + int i; + + if (r->cl == rvc_zero) + return false; + + /* For numbers (-0.5,0) and (0,0.5). */ + if (REAL_EXP (r) < 0) + return false; + + else if (REAL_EXP (r) < SIGNIFICAND_BITS) + { + unsigned int n = SIGNIFICAND_BITS - REAL_EXP (r) - 1; + int w = n / HOST_BITS_PER_LONG; + + for (i = 0; i < w; ++i) + if (r->sig[i] != 0) + return false; + + unsigned long num = ((unsigned long)1 << (n % HOST_BITS_PER_LONG)); + + if (((r->sig[w] & num) != 0) && ((r->sig[w] & (num-1)) == 0)) + return true; + } + return false; +} + +/* Round X to nearest integer, rounding halfway cases towards even. */ + +void +real_roundeven (REAL_VALUE_TYPE *r, format_helper fmt, + const REAL_VALUE_TYPE *x) +{ + if (is_halfway_below (x)) + { + /* Special case as -0.5 rounds to -0.0 and + similarly +0.5 rounds to +0.0. */ + if (REAL_EXP (x) == 0) + { + *r = *x; + clear_significand_below (r, SIGNIFICAND_BITS); + } + else + { + do_add (r, x, &dconsthalf, x->sign); + if (!is_even (r)) + do_add (r, r, &dconstm1, x->sign); + } + if (fmt) + real_convert (r, fmt, r); + } + else + real_round (r, fmt, x); +} + /* Set the sign of R to the sign of X. */ void |