aboutsummaryrefslogtreecommitdiff
path: root/gcc/range-op-float.cc
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2022-10-13 15:54:17 +0200
committerMartin Liska <mliska@suse.cz>2022-10-13 15:54:17 +0200
commitbd21c04269deded2c7476ceca1100a26f28ea526 (patch)
tree197bf75eedac69362078a4ccc0afe5615c45c327 /gcc/range-op-float.cc
parentd9e7934d25da4a78ffef1f738206aa1d897911df (diff)
parent786e4c024f941671a233f5779d73a5d22f4e9588 (diff)
downloadgcc-bd21c04269deded2c7476ceca1100a26f28ea526.zip
gcc-bd21c04269deded2c7476ceca1100a26f28ea526.tar.gz
gcc-bd21c04269deded2c7476ceca1100a26f28ea526.tar.bz2
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r--gcc/range-op-float.cc607
1 files changed, 556 insertions, 51 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 68578aa..229b9d2 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -315,17 +315,17 @@ class foperator_identity : public range_operator_float
{
using range_operator_float::fold_range;
using range_operator_float::op1_range;
-
+public:
bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED,
const frange &op1, const frange &op2 ATTRIBUTE_UNUSED,
- relation_kind) const final override
+ relation_kind = VREL_VARYING) const final override
{
r = op1;
return true;
}
bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED,
const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED,
- relation_kind) const final override
+ relation_kind = VREL_VARYING) const final override
{
r = lhs;
return true;
@@ -338,20 +338,20 @@ class foperator_equal : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
+public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return equal_op1_op2_relation (lhs);
}
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override
+ relation_kind rel = VREL_VARYING) const final override
{
return op1_range (r, type, lhs, op1, rel);
}
@@ -444,17 +444,17 @@ class foperator_not_equal : public range_operator_float
{
using range_operator_float::fold_range;
using range_operator_float::op1_range;
-
+public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind rel = VREL_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return not_equal_op1_op2_relation (lhs);
}
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
} fop_not_equal;
bool
@@ -497,14 +497,17 @@ bool
foperator_not_equal::op1_range (frange &r, tree type,
const irange &lhs,
const frange &op2,
- relation_kind) const
+ relation_kind rel) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
+ // The TRUE side of op1 != op1 implies op1 is NAN.
+ if (rel == VREL_EQ)
+ r.set_nan (type);
// If the result is true, the only time we know anything is if
// OP2 is a constant.
- if (op2.singleton_p ())
+ else if (op2.singleton_p ())
{
// This is correct even if op1 is NAN, because the following
// range would be ~[tmp, tmp] with the NAN property set to
@@ -542,20 +545,20 @@ class foperator_lt : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
+public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return lt_op1_op2_relation (lhs);
}
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
} fop_lt;
bool
@@ -657,20 +660,20 @@ class foperator_le : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
+public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind rel = VREL_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return le_op1_op2_relation (lhs);
}
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind rel = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override;
+ relation_kind rel = VREL_VARYING) const final override;
} fop_le;
bool
@@ -764,20 +767,20 @@ class foperator_gt : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
+public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return gt_op1_op2_relation (lhs);
}
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
} fop_gt;
bool
@@ -879,20 +882,20 @@ class foperator_ge : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
+public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return ge_op1_op2_relation (lhs);
}
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
} fop_ge;
bool
@@ -990,17 +993,16 @@ class foperator_unordered : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override
+ relation_kind rel = VREL_VARYING) const final override
{
return op1_range (r, type, lhs, op1, rel);
}
@@ -1026,23 +1028,27 @@ bool
foperator_unordered::op1_range (frange &r, tree type,
const irange &lhs,
const frange &op2,
- relation_kind) const
+ relation_kind rel) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
+ if (rel == VREL_EQ)
+ r.set_nan (type);
// Since at least one operand must be NAN, if one of them is
// not, the other must be.
- if (!op2.maybe_isnan ())
+ else if (!op2.maybe_isnan ())
r.set_nan (type);
else
r.set_varying (type);
break;
case BRS_FALSE:
+ if (rel == VREL_EQ)
+ r.clear_nan ();
// A false UNORDERED means both operands are !NAN, so it's
// impossible for op2 to be a NAN.
- if (op2.known_isnan ())
+ else if (op2.known_isnan ())
r.set_undefined ();
else
{
@@ -1064,17 +1070,16 @@ class foperator_ordered : public range_operator_float
using range_operator_float::fold_range;
using range_operator_float::op1_range;
using range_operator_float::op2_range;
-
public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
- relation_kind rel) const final override;
+ relation_kind = VREL_VARYING) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override
+ relation_kind rel = VREL_VARYING) const final override
{
return op1_range (r, type, lhs, op1, rel);
}
@@ -1127,25 +1132,481 @@ foperator_ordered::op1_range (frange &r, tree type,
return true;
}
-// Placeholder for unimplemented relational operators.
+class foperator_negate : public range_operator_float
+{
+ using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+public:
+ bool fold_range (frange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind = VREL_VARYING) const final override
+ {
+ if (empty_range_varying (r, type, op1, op2))
+ return true;
+ if (op1.known_isnan ())
+ {
+ bool sign;
+ if (op1.nan_signbit_p (sign))
+ r.set_nan (type, !sign);
+ else
+ r.set_nan (type);
+ return true;
+ }
+
+ REAL_VALUE_TYPE lh_lb = op1.lower_bound ();
+ REAL_VALUE_TYPE lh_ub = op1.upper_bound ();
+ lh_lb = real_value_negate (&lh_lb);
+ lh_ub = real_value_negate (&lh_ub);
+ r.set (type, lh_ub, lh_lb);
+ if (op1.maybe_isnan ())
+ {
+ bool sign;
+ if (op1.nan_signbit_p (sign))
+ r.update_nan (!sign);
+ else
+ r.update_nan ();
+ }
+ else
+ r.clear_nan ();
+ return true;
+ }
+ bool op1_range (frange &r, tree type,
+ const frange &lhs, const frange &op2,
+ relation_kind rel = VREL_VARYING) const final override
+ {
+ return fold_range (r, type, lhs, op2, rel);
+ }
+} fop_negate;
+
+class foperator_abs : public range_operator_float
+{
+ using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+public:
+ bool fold_range (frange &r, tree type,
+ const frange &op1, const frange &,
+ relation_kind = VREL_VARYING) const final override;
+ bool op1_range (frange &r, tree type,
+ const frange &lhs, const frange &op2,
+ relation_kind rel = VREL_VARYING) const final override;
+} fop_abs;
+
+bool
+foperator_abs::fold_range (frange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind) const
+{
+ if (empty_range_varying (r, type, op1, op2))
+ return true;
+ if (op1.known_isnan ())
+ {
+ r.set_nan (type, /*sign=*/false);
+ return true;
+ }
+
+ const REAL_VALUE_TYPE lh_lb = op1.lower_bound ();
+ const REAL_VALUE_TYPE lh_ub = op1.upper_bound ();
+ // Handle the easy case where everything is positive.
+ if (real_compare (GE_EXPR, &lh_lb, &dconst0)
+ && !real_iszero (&lh_lb, /*sign=*/true)
+ && !op1.maybe_isnan (/*sign=*/true))
+ {
+ r = op1;
+ return true;
+ }
+
+ REAL_VALUE_TYPE min = real_value_abs (&lh_lb);
+ REAL_VALUE_TYPE max = real_value_abs (&lh_ub);
+ // If the range contains zero then we know that the minimum value in the
+ // range will be zero.
+ if (real_compare (LE_EXPR, &lh_lb, &dconst0)
+ && real_compare (GE_EXPR, &lh_ub, &dconst0))
+ {
+ if (real_compare (GT_EXPR, &min, &max))
+ max = min;
+ min = dconst0;
+ }
+ else
+ {
+ // If the range was reversed, swap MIN and MAX.
+ if (real_compare (GT_EXPR, &min, &max))
+ std::swap (min, max);
+ }
+
+ r.set (type, min, max);
+ if (op1.maybe_isnan ())
+ r.update_nan (/*sign=*/false);
+ else
+ r.clear_nan ();
+ return true;
+}
+
+bool
+foperator_abs::op1_range (frange &r, tree type,
+ const frange &lhs, const frange &op2,
+ relation_kind) const
+{
+ if (empty_range_varying (r, type, lhs, op2))
+ return true;
+ if (lhs.known_isnan ())
+ {
+ r.set_nan (type);
+ return true;
+ }
+
+ // Start with the positives because negatives are an impossible result.
+ frange positives (type, dconst0, frange_val_max (type));
+ positives.update_nan (/*sign=*/false);
+ positives.intersect (lhs);
+ r = positives;
+ // Then add the negative of each pair:
+ // ABS(op1) = [5,20] would yield op1 => [-20,-5][5,20].
+ r.union_ (frange (type,
+ real_value_negate (&positives.upper_bound ()),
+ real_value_negate (&positives.lower_bound ())));
+ return true;
+}
+
+class foperator_unordered_lt : public range_operator_float
+{
+ using range_operator_float::fold_range;
+public:
+ bool fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel = VREL_VARYING) const final override
+ {
+ if (op1.known_isnan () || op2.known_isnan ())
+ {
+ r = range_true (type);
+ return true;
+ }
+ if (!fop_lt.fold_range (r, type, op1, op2, rel))
+ return false;
+ // The result is the same as the ordered version when the
+ // comparison is true or when the operands cannot be NANs.
+ if (finite_operands_p (op1, op2) || r == range_true (type))
+ return true;
+ else
+ {
+ r = range_true_and_false (type);
+ return true;
+ }
+ }
+} fop_unordered_lt;
-class foperator_relop_unknown : public range_operator_float
+class foperator_unordered_le : public range_operator_float
{
using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+ using range_operator_float::op2_range;
+public:
+ bool fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel = VREL_VARYING) const final override
+ {
+ if (op1.known_isnan () || op2.known_isnan ())
+ {
+ r = range_true (type);
+ return true;
+ }
+ if (!fop_le.fold_range (r, type, op1, op2, rel))
+ return false;
+ // The result is the same as the ordered version when the
+ // comparison is true or when the operands cannot be NANs.
+ if (finite_operands_p (op1, op2) || r == range_true (type))
+ return true;
+ else
+ {
+ r = range_true_and_false (type);
+ return true;
+ }
+ }
+ bool op1_range (frange &r, tree type,
+ const irange &lhs, const frange &op2,
+ relation_kind = VREL_VARYING) const final override;
+ bool op2_range (frange &r, tree type,
+ const irange &lhs, const frange &op1,
+ relation_kind = VREL_VARYING) const final override;
+} fop_unordered_le;
+
+bool
+foperator_unordered_le::op1_range (frange &r, tree type,
+ const irange &lhs, const frange &op2,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ build_le (r, type, op2);
+ break;
+ case BRS_FALSE:
+ build_gt (r, type, op2);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+foperator_unordered_le::op2_range (frange &r,
+ tree type,
+ const irange &lhs,
+ const frange &op1,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ build_ge (r, type, op1);
+ break;
+
+ case BRS_FALSE:
+ build_lt (r, type, op1);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+class foperator_unordered_gt : public range_operator_float
+{
+ using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+ using range_operator_float::op2_range;
public:
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind) const final override
+ relation_kind rel = VREL_VARYING) const final override
{
if (op1.known_isnan () || op2.known_isnan ())
- r = range_true (type);
+ {
+ r = range_true (type);
+ return true;
+ }
+ if (!fop_gt.fold_range (r, type, op1, op2, rel))
+ return false;
+ // The result is the same as the ordered version when the
+ // comparison is true or when the operands cannot be NANs.
+ if (finite_operands_p (op1, op2) || r == range_true (type))
+ return true;
else
- r.set_varying (type);
- return true;
+ {
+ r = range_true_and_false (type);
+ return true;
+ }
+ }
+ bool op1_range (frange &r, tree type,
+ const irange &lhs, const frange &op2,
+ relation_kind = VREL_VARYING) const final override;
+ bool op2_range (frange &r, tree type,
+ const irange &lhs, const frange &op1,
+ relation_kind = VREL_VARYING) const final override;
+} fop_unordered_gt;
+
+bool
+foperator_unordered_gt::op1_range (frange &r,
+ tree type,
+ const irange &lhs,
+ const frange &op2,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ build_gt (r, type, op2);
+ break;
+
+ case BRS_FALSE:
+ build_le (r, type, op2);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+foperator_unordered_gt::op2_range (frange &r,
+ tree type,
+ const irange &lhs,
+ const frange &op1,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ build_lt (r, type, op1);
+ break;
+
+ case BRS_FALSE:
+ build_ge (r, type, op1);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+class foperator_unordered_ge : public range_operator_float
+{
+ using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+ using range_operator_float::op2_range;
+public:
+ bool fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel = VREL_VARYING) const final override
+ {
+ if (op1.known_isnan () || op2.known_isnan ())
+ {
+ r = range_true (type);
+ return true;
+ }
+ if (!fop_ge.fold_range (r, type, op1, op2, rel))
+ return false;
+ // The result is the same as the ordered version when the
+ // comparison is true or when the operands cannot be NANs.
+ if (finite_operands_p (op1, op2) || r == range_true (type))
+ return true;
+ else
+ {
+ r = range_true_and_false (type);
+ return true;
+ }
+ }
+ bool op1_range (frange &r, tree type,
+ const irange &lhs, const frange &op2,
+ relation_kind = VREL_VARYING) const final override;
+ bool op2_range (frange &r, tree type,
+ const irange &lhs, const frange &op1,
+ relation_kind = VREL_VARYING) const final override;
+} fop_unordered_ge;
+
+bool
+foperator_unordered_ge::op1_range (frange &r,
+ tree type,
+ const irange &lhs,
+ const frange &op2,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ build_ge (r, type, op2);
+ break;
+
+ case BRS_FALSE:
+ build_lt (r, type, op2);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+foperator_unordered_ge::op2_range (frange &r, tree type,
+ const irange &lhs,
+ const frange &op1,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ build_le (r, type, op1);
+ break;
+
+ case BRS_FALSE:
+ build_gt (r, type, op1);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+class foperator_unordered_equal : public range_operator_float
+{
+ using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+ using range_operator_float::op2_range;
+public:
+ bool fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel = VREL_VARYING) const final override
+ {
+ if (op1.known_isnan () || op2.known_isnan ())
+ {
+ r = range_true (type);
+ return true;
+ }
+ if (!fop_equal.fold_range (r, type, op1, op2, rel))
+ return false;
+ // The result is the same as the ordered version when the
+ // comparison is true or when the operands cannot be NANs.
+ if (finite_operands_p (op1, op2) || r == range_true (type))
+ return true;
+ else
+ {
+ r = range_true_and_false (type);
+ return true;
+ }
}
-} fop_unordered_relop_unknown;
+ bool op1_range (frange &r, tree type,
+ const irange &lhs, const frange &op2,
+ relation_kind = VREL_VARYING) const final override;
+ bool op2_range (frange &r, tree type,
+ const irange &lhs, const frange &op1,
+ relation_kind rel = VREL_VARYING) const final override
+ {
+ return op1_range (r, type, lhs, op1, rel);
+ }
+} fop_unordered_equal;
+
+bool
+foperator_unordered_equal::op1_range (frange &r, tree type,
+ const irange &lhs,
+ const frange &op2,
+ relation_kind) const
+{
+ switch (get_bool_state (r, lhs, type))
+ {
+ case BRS_TRUE:
+ // If it's true, the result is the same as OP2 plus a NAN.
+ r = op2;
+ // Add both zeros if there's the possibility of zero equality.
+ frange_add_zeros (r, type);
+ // Add the posibility of a NAN.
+ r.update_nan ();
+ break;
+ case BRS_FALSE:
+ // The false side indictates !NAN and not equal. We can at least
+ // represent !NAN.
+ r.set_varying (type);
+ r.clear_nan ();
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
// Instantiate a range_op_table for floating point operations.
static floating_op_table global_floating_table;
@@ -1169,13 +1630,16 @@ floating_op_table::floating_op_table ()
set (LE_EXPR, fop_le);
set (GT_EXPR, fop_gt);
set (GE_EXPR, fop_ge);
- set (UNLE_EXPR, fop_unordered_relop_unknown);
- set (UNLT_EXPR, fop_unordered_relop_unknown);
- set (UNGE_EXPR, fop_unordered_relop_unknown);
- set (UNGT_EXPR, fop_unordered_relop_unknown);
- set (UNEQ_EXPR, fop_unordered_relop_unknown);
+ set (UNLE_EXPR, fop_unordered_le);
+ set (UNLT_EXPR, fop_unordered_lt);
+ set (UNGE_EXPR, fop_unordered_ge);
+ set (UNGT_EXPR, fop_unordered_gt);
+ set (UNEQ_EXPR, fop_unordered_equal);
set (ORDERED_EXPR, fop_ordered);
set (UNORDERED_EXPR, fop_unordered);
+
+ set (ABS_EXPR, fop_abs);
+ set (NEGATE_EXPR, fop_negate);
}
// Return a pointer to the range_operator_float instance, if there is
@@ -1195,3 +1659,44 @@ floating_op_table::set (enum tree_code code, range_operator_float &op)
gcc_checking_assert (m_range_tree[code] == NULL);
m_range_tree[code] = &op;
}
+
+#if CHECKING_P
+#include "selftest.h"
+
+namespace selftest
+{
+
+// Build an frange from string endpoints.
+
+inline frange
+frange_float (const char *lb, const char *ub, tree type = float_type_node)
+{
+ REAL_VALUE_TYPE min, max;
+ gcc_assert (real_from_string (&min, lb) == 0);
+ gcc_assert (real_from_string (&max, ub) == 0);
+ return frange (type, min, max);
+}
+
+void
+range_op_float_tests ()
+{
+ frange r, r0, r1;
+ frange trange (float_type_node);
+
+ // negate([-5, +10]) => [-10, 5]
+ r0 = frange_float ("-5", "10");
+ fop_negate.fold_range (r, float_type_node, r0, trange);
+ ASSERT_EQ (r, frange_float ("-10", "5"));
+
+ // negate([0, 1] -NAN) => [-1, -0] +NAN
+ r0 = frange_float ("0", "1");
+ r0.update_nan (true);
+ fop_negate.fold_range (r, float_type_node, r0, trange);
+ r1 = frange_float ("-1", "-0");
+ r1.update_nan (false);
+ ASSERT_EQ (r, r1);
+}
+
+} // namespace selftest
+
+#endif // CHECKING_P