Hello. Taking the notes from Joseph under consideration, I have developed a fairly working patch for roundeven, attached herewith. I have done bit-wise calculations to check for halfway cases, though HOST_WIDE_INT is only used to check for even and odd numbers (or is it necessary to do bit-wise for this too?). Also, why unsigned long sig[SIGSZ] in real_value has to be an array? (for 64 bit system, its an array of size 3, mostly first 2 values being 0?). Thanks.
Regards, -Tejas
diff --git a/gcc/builtins.c b/gcc/builtins.c index 25e01e4092b..0b2d6bf82f9 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -2067,6 +2067,7 @@ mathfn_built_in_2 (tree type, combined_fn fn) CASE_MATHFN (REMQUO) CASE_MATHFN_FLOATN (RINT) CASE_MATHFN_FLOATN (ROUND) + CASE_MATHFN (ROUNDEVEN) CASE_MATHFN (SCALB) CASE_MATHFN (SCALBLN) CASE_MATHFN (SCALBN) diff --git a/gcc/builtins.def b/gcc/builtins.def index ef89729fd0c..e1d593a8765 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -542,6 +542,9 @@ DEF_C99_BUILTIN (BUILT_IN_RINTL, "rintl", BT_FN_LONGDOUBLE_LONGDOUBLE, AT #define RINT_TYPE(F) BT_FN_##F##_##F DEF_EXT_LIB_FLOATN_NX_BUILTINS (BUILT_IN_RINT, "rint", RINT_TYPE, ATTR_CONST_NOTHROW_LEAF_LIST) #undef RINT_TYPE +DEF_EXT_LIB_BUILTIN (BUILT_IN_ROUNDEVEN, "roundeven", BT_FN_DOUBLE_DOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_ROUNDEVENF, "roundevenf", BT_FN_FLOAT_FLOAT, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_ROUNDEVENL, "roundevenl", BT_FN_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN_ROUND, "round", BT_FN_DOUBLE_DOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN_ROUNDF, "roundf", BT_FN_FLOAT_FLOAT, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN_ROUNDL, "roundl", BT_FN_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST) diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c index 06a420601c0..7eafd91e9a2 100644 --- a/gcc/fold-const-call.c +++ b/gcc/fold-const-call.c @@ -792,6 +792,14 @@ fold_const_call_ss (real_value *result, combined_fn fn, } return false; + case CFN_BUILT_IN_ROUNDEVEN: + if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) + { + real_roundeven (result, format, arg); + return true; + } + return false; + CASE_CFN_LOGB: return fold_const_logb (result, arg, format); @@ -854,6 +862,9 @@ fold_const_call_ss (wide_int *result, combined_fn fn, return fold_const_conversion (result, real_round, arg, precision, format); + case CFN_BUILT_IN_ROUNDEVEN: + return fold_const_conversion (result, real_roundeven, arg, precision, format); + CASE_CFN_IRINT: CASE_CFN_LRINT: CASE_CFN_LLRINT: diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 59cedeafd71..30c409e95bf 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -329,6 +329,7 @@ negate_mathfn_p (combined_fn fn) CASE_CFN_LLROUND: CASE_CFN_LROUND: CASE_CFN_ROUND: + CASE_CFN_ROUNDEVEN: CASE_CFN_SIN: CASE_CFN_SINH: CASE_CFN_TAN: @@ -13060,6 +13061,8 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1, CASE_CFN_RINT_FN: CASE_CFN_ROUND: CASE_CFN_ROUND_FN: + CASE_CFN_ROUNDEVEN: + CASE_CFN_ROUNDEVEN_FN: CASE_CFN_SCALB: CASE_CFN_SCALBLN: CASE_CFN_SCALBN: @@ -13583,6 +13586,8 @@ integer_valued_real_call_p (combined_fn fn, tree arg0, tree arg1, int depth) CASE_CFN_RINT_FN: CASE_CFN_ROUND: CASE_CFN_ROUND_FN: + CASE_CFN_ROUNDEVEN: + CASE_CFN_ROUNDEVEN_FN: CASE_CFN_TRUNC: CASE_CFN_TRUNC_FN: return true; diff --git a/gcc/real.c b/gcc/real.c index f822ae82d61..533d471a89b 100644 --- a/gcc/real.c +++ b/gcc/real.c @@ -5010,6 +5010,43 @@ real_round (REAL_VALUE_TYPE *r, format_helper fmt, real_convert (r, fmt, r); } +bool +is_halfway_below (const REAL_VALUE_TYPE *r) +{ + unsigned long tempsig[SIGSZ]; + unsigned int n = SIGNIFICAND_BITS - REAL_EXP (r); + int i, w = n / HOST_BITS_PER_LONG; + + for (i = 0; i < SIGSZ; ++i) + tempsig[i] = r->sig[i]; + + for (i = 0; i < w; ++i) + tempsig[i] = 0; + + tempsig[w] &= (((unsigned long)1 << ((n % HOST_BITS_PER_LONG) - 1)) - 1); + + if (tempsig[w] == 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)) + { + do_add (r, x, &dconsthalf, x->sign); + HOST_WIDE_INT i = real_to_integer (r); + if (i % 2) + do_add (r, r, &dconstm1, x->sign); + } + else + real_round (r, fmt, x); +} + /* Set the sign of R to the sign of X. */ void diff --git a/gcc/real.h b/gcc/real.h index 0ce42565708..10898eae79e 100644 --- a/gcc/real.h +++ b/gcc/real.h @@ -499,6 +499,8 @@ extern void real_ceil (REAL_VALUE_TYPE *, format_helper, const REAL_VALUE_TYPE *); extern void real_round (REAL_VALUE_TYPE *, format_helper, const REAL_VALUE_TYPE *); +extern void real_roundeven (REAL_VALUE_TYPE *, format_helper, + const REAL_VALUE_TYPE *); /* Set the sign of R to the sign of X. */ extern void real_copysign (REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);