diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index ff9fe312acf33dda06ff934948d05d064d0d863c..ca41136f4cd3d6979eb3c92520c94f8595f6e8d6 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -150,6 +150,18 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons
   return VREL_VARYING;
 }
 
+// Set R to [NAN, NAN].
+
+static inline void
+frange_set_nan (frange &r, tree type)
+{
+  REAL_VALUE_TYPE rv;
+  bool res = real_nan (&rv, "", 1, TYPE_MODE (type));
+  if (flag_checking)
+    gcc_assert (res);
+  r.set (type, rv, rv);
+}
+
 // Return TRUE if OP1 and OP2 are known to be free of NANs.
 
 static inline bool
@@ -178,6 +190,40 @@ frelop_early_resolve (irange &r, tree type,
 	  && relop_early_resolve (r, type, op1, op2, rel, my_rel));
 }
 
+// Crop R to [-INF, MAX] where MAX is the maximum representable number
+// for TYPE.
+
+static inline void
+frange_drop_inf (frange &r, tree type)
+{
+  // FIXME: build_real() bails on decimal float modes when called with
+  // a max representable endpoint.
+  if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type)))
+    return;
+
+  REAL_VALUE_TYPE max;
+  real_max_representable (&max, type);
+  frange tmp (type, r.lower_bound (), max);
+  r.intersect (tmp);
+}
+
+// Crop R to [MIN, +INF] where MIN is the minimum representable number
+// for TYPE.
+
+static inline void
+frange_drop_ninf (frange &r, tree type)
+{
+  // FIXME: build_real() bails on decimal float modes when called with
+  // a max representable endpoint.
+  if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type)))
+    return;
+
+  REAL_VALUE_TYPE min;
+  real_min_representable (&min, type);
+  frange tmp (type, min, r.upper_bound ());
+  r.intersect (tmp);
+}
+
 // Default implementation of fold_range for relational operators.
 // This amounts to passing on any known relations from the oracle, iff
 // we know the operands are not NAN or -ffinite-math-only holds.
@@ -252,21 +298,8 @@ foperator_equal::op1_range (frange &r, tree type,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      if (HONOR_SIGNED_ZEROS (type)
-	  && op2.contains_p (build_zero_cst (type)))
-	{
-	  // With signed zeros, x == -0.0 does not mean we can replace
-	  // x with -0.0, because x may be either +0.0 or -0.0.
-	  r.set_varying (type);
-	}
-      else
-	{
-	  // If it's true, the result is the same as OP2.
-	  //
-	  // If the range does not actually contain zeros, this should
-	  // always be OK.
-	  r = op2;
-	}
+      // If it's true, the result is the same as OP2.
+      r = op2;
       // The TRUE side of op1 == op2 implies op1 is !NAN.
       r.set_nan (fp_prop::NO);
       break;
@@ -275,7 +308,7 @@ foperator_equal::op1_range (frange &r, tree type,
       r.set_varying (type);
       // The FALSE side of op1 == op1 implies op1 is a NAN.
       if (rel == VREL_EQ)
-	r.set_nan (fp_prop::YES);
+	frange_set_nan (r, type);
       break;
 
     default:
@@ -365,7 +398,8 @@ foperator_lt::op1_range (frange &r,
       r.set_varying (type);
       // The TRUE side of op1 < op2 implies op1 is !NAN and !INF.
       r.set_nan (fp_prop::NO);
-      r.set_inf (fp_prop::NO);
+      // x < y implies x is not +INF.
+      frange_drop_inf (r, type);
       break;
 
     case BRS_FALSE:
@@ -391,7 +425,8 @@ foperator_lt::op2_range (frange &r,
       r.set_varying (type);
       // The TRUE side of op1 < op2 implies op2 is !NAN and !NINF.
       r.set_nan (fp_prop::NO);
-      r.set_ninf (fp_prop::NO);
+      // x < y implies y is not -INF.
+      frange_drop_ninf (r, type);
       break;
 
     case BRS_FALSE:
@@ -493,7 +528,8 @@ foperator_gt::op1_range (frange &r,
       r.set_varying (type);
       // The TRUE side of op1 > op2 implies op1 is !NAN and !NINF.
       r.set_nan (fp_prop::NO);
-      r.set_ninf (fp_prop::NO);
+      // x > y implies x is not -INF.
+      frange_drop_ninf (r, type);
       break;
 
     case BRS_FALSE:
@@ -519,7 +555,8 @@ foperator_gt::op2_range (frange &r,
       r.set_varying (type);
       // The TRUE side of op1 > op2 implies op2 is !NAN and !INF.
       r.set_nan (fp_prop::NO);
-      r.set_inf (fp_prop::NO);
+      // x > y implies y is not +INF.
+      frange_drop_inf (r, type);
       break;
 
     case BRS_FALSE:
@@ -636,7 +673,7 @@ foperator_unordered::op1_range (frange &r, tree type,
       // Since at least one operand must be NAN, if one of them is
       // not, the other must be.
       if (op2.get_nan ().no_p ())
-	r.set_nan (fp_prop::YES);
+	frange_set_nan (r, type);
       break;
 
     case BRS_FALSE:
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 4af8eca0172f6d940c7c0264faf95fe3762057ee..4637fb409e5f4562c8f91414d667cdaa8da7377b 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -211,10 +211,19 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt)
   switch (TREE_CODE (expr))
     {
     case INTEGER_CST:
+      if (TREE_OVERFLOW_P (expr))
+	expr = drop_tree_overflow (expr);
+      r.set (expr, expr);
+      return true;
+
     case REAL_CST:
       if (TREE_OVERFLOW_P (expr))
 	expr = drop_tree_overflow (expr);
       r.set (expr, expr);
+      if (real_isnan (TREE_REAL_CST_PTR (expr)))
+	as_a <frange> (r).set_nan (fp_prop::YES);
+      else
+	as_a <frange> (r).set_nan (fp_prop::NO);
       return true;
 
     case SSA_NAME:
diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index cbf50d3d854cb333ca1ca162d82de7b909d3d343..e66d56da29d59da416be41a4613b1b37340b86c3 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -122,22 +122,30 @@ vrange_printer::print_irange_bitmasks (const irange &r) const
 void
 vrange_printer::visit (const frange &r) const
 {
+  tree type = r.type ();
+
   pp_string (pp, "[frange] ");
   if (r.undefined_p ())
     {
       pp_string (pp, "UNDEFINED");
       return;
     }
-  dump_generic_node (pp, r.type (), 0, TDF_NONE, false);
+  dump_generic_node (pp, type, 0, TDF_NONE, false);
   pp_string (pp, " ");
   if (r.varying_p ())
     {
       pp_string (pp, "VARYING");
       return;
     }
+  pp_character (pp, '[');
+  dump_generic_node (pp,
+		     build_real (type, r.lower_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, ", ");
+  dump_generic_node (pp,
+		     build_real (type, r.upper_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, "] ");
+
   print_frange_prop ("NAN", r.get_nan ());
-  print_frange_prop ("INF", r.get_inf ());
-  print_frange_prop ("NINF", r.get_ninf ());
 }
 
 // Print the FP properties in an frange.
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index ea3b83ca641dbe7cd873eddf4e4862e9bae6cfd8..adf23c39f0de529962c60db3bc7646b551c38c22 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -261,6 +261,18 @@ frange_storage_slot::get_frange (frange &r, tree type) const
 {
   gcc_checking_assert (r.supports_type_p (type));
 
+  // FIXME: NANs get special treatment, because we need [NAN, NAN] in
+  // the range to properly represent it, not just the NAN flag in the
+  // property bits.  This will go away when we stream out the
+  // endpoints.
+  if (m_props.get_nan ().yes_p ())
+    {
+      REAL_VALUE_TYPE rv;
+      real_nan (&rv, "", 1, TYPE_MODE (type));
+      r.set (type, rv, rv);
+      return;
+    }
+
   r.set_varying (type);
   r.m_props = m_props;
   r.normalize_kind ();
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index edd10bf57947a7ca04938cb682a6d60e56f55ab3..bcc6651701b441da3c305cf5c924ca7788225b68 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -291,41 +291,20 @@ frange::set (tree min, tree max, value_range_kind kind)
   m_kind = kind;
   m_type = TREE_TYPE (min);
   m_props.set_varying ();
+  m_min = *TREE_REAL_CST_PTR (min);
+  m_max = *TREE_REAL_CST_PTR (max);
 
-  bool is_min = vrp_val_is_min (min);
-  bool is_max = vrp_val_is_max (max);
   bool is_nan = (real_isnan (TREE_REAL_CST_PTR (min))
 		 || real_isnan (TREE_REAL_CST_PTR (max)));
 
   // Ranges with a NAN and a non-NAN endpoint are nonsensical.
   gcc_checking_assert (!is_nan || operand_equal_p (min, max));
 
-  // The properties for singletons can be all set ahead of time.
-  if (operand_equal_p (min, max))
-    {
-      // Set INF properties.
-      if (is_min)
-	m_props.ninf_set_yes ();
-      else
-	m_props.ninf_set_no ();
-      if (is_max)
-	m_props.inf_set_yes ();
-      else
-	m_props.inf_set_no ();
-      // Set NAN property.
-      if (is_nan)
-	m_props.nan_set_yes ();
-      else
-	m_props.nan_set_no ();
-    }
-  else
-    {
-      // Mark when the endpoints can't be +-INF.
-      if (!is_min)
-	m_props.ninf_set_no ();
-      if (!is_max)
-	m_props.inf_set_no ();
-    }
+  // Set NAN property if we're absolutely sure.
+  if (is_nan && operand_equal_p (min, max))
+    m_props.nan_set_yes ();
+  else if (!HONOR_NANS (m_type))
+    m_props.nan_set_no ();
 
   // Check for swapped ranges.
   gcc_checking_assert (is_nan || tree_compare (LE_EXPR, min, max));
@@ -336,6 +315,16 @@ frange::set (tree min, tree max, value_range_kind kind)
     verify_range ();
 }
 
+// Setter for frange from REAL_VALUE_TYPE endpoints.
+
+void
+frange::set (tree type,
+	     const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	     value_range_kind kind)
+{
+  set (build_real (type, min), build_real (type, max), kind);
+}
+
 // Normalize range to VARYING or UNDEFINED, or vice versa.  Return
 // TRUE if anything changed.
 //
@@ -347,7 +336,15 @@ frange::set (tree min, tree max, value_range_kind kind)
 bool
 frange::normalize_kind ()
 {
-  if (m_kind == VR_RANGE)
+  // Undefined is viral.
+  if (m_props.nan_undefined_p ())
+    {
+      set_undefined ();
+      return true;
+    }
+  if (m_kind == VR_RANGE
+      && real_isinf (&m_min, 1)
+      && real_isinf (&m_max, 0))
     {
       // No FP properties set means varying.
       if (m_props.varying_p ())
@@ -355,14 +352,6 @@ frange::normalize_kind ()
 	  set_varying (m_type);
 	  return true;
 	}
-      // Undefined is viral.
-      if (m_props.nan_undefined_p ()
-	  || m_props.inf_undefined_p ()
-	  || m_props.ninf_undefined_p ())
-	{
-	  set_undefined ();
-	  return true;
-	}
     }
   else if (m_kind == VR_VARYING)
     {
@@ -370,12 +359,32 @@ frange::normalize_kind ()
       if (!m_props.varying_p ())
 	{
 	  m_kind = VR_RANGE;
+	  real_inf (&m_min, 1);
+	  real_inf (&m_max, 0);
 	  return true;
 	}
     }
   return false;
 }
 
+// If both operands are definitely NAN, do nothing as they combine
+// perfectly.  If OTOH, only one is a NAN, set R to VARYING as they
+// can't be neither unioned nor intersected.  Return TRUE if we
+// changed anything.
+
+static inline bool
+early_nan_resolve (frange &r, const frange &other)
+{
+  gcc_checking_assert (r.get_nan ().yes_p () || other.get_nan ().yes_p ());
+
+  // There's nothing to do for both NANs.
+  if (r.get_nan ().yes_p () == other.get_nan ().yes_p ())
+    return false;
+  // But only one NAN complicates things.
+  r.set_varying (r.type ());
+  return true;
+}
+
 bool
 frange::union_ (const vrange &v)
 {
@@ -388,13 +397,34 @@ frange::union_ (const vrange &v)
       *this = r;
       return true;
     }
+  // ?? We could do better here.  [5,6] U NAN should be [5,6] with the
+  // NAN maybe bits set.  For now, this is conservatively correct.
+  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+    return early_nan_resolve (*this, r);
 
-  bool ret = m_props.union_ (r.m_props);
-  ret |= normalize_kind ();
+  bool changed = m_props.union_ (r.m_props);
+
+  // Note: for the combination of zeros that differ in sign we keep
+  // the endpoints untouched.  This means that for -0.0 U +0.0, the
+  // result will be -0.0.  Ultimately this doesn't matter, because we
+  // keep singleton zeros ambiguous throughout to avoid propagating
+  // them.  See frange::singleton_p().
+
+  if (real_less (&r.m_min, &m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&m_max, &r.m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 bool
@@ -414,13 +444,38 @@ frange::intersect (const vrange &v)
       *this = r;
       return true;
     }
+  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+    return early_nan_resolve (*this, r);
+
+  bool changed = m_props.intersect (r.m_props);
+
+  // Note: for the combination of zeros that differ in sign we keep
+  // the endpoints untouched.  This means that for -0.0 ^ +0.0, the
+  // result will be -0.0.  Ultimately this doesn't matter, because we
+  // keep singleton zeros ambiguous throughout to avoid propagating
+  // them.  See frange::singleton_p().
 
-  bool ret = m_props.intersect (r.m_props);
-  ret |= normalize_kind ();
+  if (real_less (&m_min, &r.m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&r.m_max, &m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  // If the endpoints are swapped, the ranges are disjoint.
+  if (real_less (&m_max, &m_min))
+    {
+      set_undefined ();
+      return true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 frange &
@@ -428,6 +483,8 @@ frange::operator= (const frange &src)
 {
   m_kind = src.m_kind;
   m_type = src.m_type;
+  m_min = src.m_min;
+  m_max = src.m_max;
   m_props = src.m_props;
 
   if (flag_checking)
@@ -446,7 +503,59 @@ frange::operator== (const frange &src) const
       if (varying_p ())
 	return types_compatible_p (m_type, src.m_type);
 
-      return m_props == src.m_props;
+      if (m_props.get_nan ().yes_p ()
+	  || src.m_props.get_nan ().yes_p ())
+	return false;
+
+      return (real_identical (&m_min, &src.m_min)
+	      && real_identical (&m_max, &src.m_max)
+	      && m_props == src.m_props
+	      && types_compatible_p (m_type, src.m_type));
+    }
+  return false;
+}
+
+// Return TRUE if range contains the TREE_REAL_CST_PTR in CST.
+
+bool
+frange::contains_p (tree cst) const
+{
+  if (undefined_p ())
+    return false;
+
+  if (varying_p ())
+    return true;
+
+  gcc_checking_assert (m_kind == VR_RANGE);
+
+  return (real_compare (GE_EXPR, TREE_REAL_CST_PTR (cst), &m_min)
+	  && real_compare (LE_EXPR, TREE_REAL_CST_PTR (cst), &m_max));
+}
+
+// If range is a singleton, place it in RESULT and return TRUE.  If
+// RESULT is NULL, just return TRUE.
+//
+// A NAN can never be a singleton, and neither can a 0.0 if we're
+// honoring signed zeros because we have no way of telling which zero
+// it is.
+
+bool
+frange::singleton_p (tree *result) const
+{
+  if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
+    {
+      // If we're honoring signed zeros, fail because we don't know
+      // which zero we have.  This avoids propagating the wrong zero.
+      if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
+	return false;
+
+      // Return false for any singleton that may be a NAN.
+      if (HONOR_NANS (m_type) && !get_nan ().no_p ())
+	return false;
+
+      if (result)
+	*result = build_real (m_type, m_min);
+      return true;
     }
   return false;
 }
@@ -465,13 +574,82 @@ frange::verify_range ()
       gcc_checking_assert (m_props.undefined_p ());
       return;
     }
+  gcc_checking_assert (!m_props.undefined_p ());
+
   if (varying_p ())
     {
       gcc_checking_assert (m_props.varying_p ());
       return;
     }
+
+  // We don't support the inverse of an frange (yet).
   gcc_checking_assert (m_kind == VR_RANGE);
-  gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ());
+
+  bool is_nan = real_isnan (&m_min) || real_isnan (&m_max);
+  if (is_nan)
+    {
+      // If either is a NAN, both must be a NAN.
+      gcc_checking_assert (real_identical (&m_min, &m_max));
+      gcc_checking_assert (get_nan ().yes_p ());
+    }
+  else
+    // Make sure we don't have swapped ranges.
+    gcc_checking_assert (!real_less (&m_max, &m_min));
+
+  // If we're absolutely sure we have a NAN, the endpoints should
+  // reflect this, otherwise we'd have more than one way to represent
+  // a NAN.
+  if (m_props.get_nan ().yes_p ())
+    {
+      gcc_checking_assert (real_isnan (&m_min));
+      gcc_checking_assert (real_isnan (&m_max));
+    }
+
+  // If all the properties are clear, we better not span the entire
+  // domain, because that would make us varying.
+  if (m_props.varying_p ())
+    gcc_checking_assert (!real_isinf (&m_min, 1) || !real_isinf (&m_max, 0));
+}
+
+// We can't do much with nonzeros yet.
+void
+frange::set_nonzero (tree type)
+{
+  set_varying (type);
+}
+
+// We can't do much with nonzeros yet.
+bool
+frange::nonzero_p () const
+{
+  return false;
+}
+
+// Set range to [+0.0, +0.0].
+
+void
+frange::set_zero (tree type)
+{
+  tree zero = build_zero_cst (type);
+  set (zero, zero);
+}
+
+// Return TRUE for any [0.0, 0.0] regardless of sign.
+
+bool
+frange::zero_p () const
+{
+  return (m_kind == VR_RANGE
+	  && real_iszero (&m_min)
+	  && real_iszero (&m_max));
+}
+
+void
+frange::set_nonnegative (tree type)
+{
+  tree zero = build_zero_cst (type);
+  tree inf = vrp_val_max (type);
+  set (zero, inf);
 }
 
 // Here we copy between any two irange's.  The ranges can be legacy or
@@ -3304,6 +3482,199 @@ range_tests_nonzero_bits ()
   ASSERT_TRUE (r0.varying_p ());
 }
 
+// Build an frange from string endpoints.
+
+static 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);
+}
+
+// Build a NAN of type TYPE.
+
+static inline frange
+frange_nan (tree type = float_type_node)
+{
+  REAL_VALUE_TYPE r;
+
+  gcc_assert (real_nan (&r, "", 1, TYPE_MODE (type)));
+  return frange (type, r, r);
+}
+
+static void
+range_tests_nan ()
+{
+  frange r0, r1;
+
+  // Equal ranges but with differing NAN bits are not equal.
+  r1 = frange_float ("10", "12");
+  r0 = r1;
+  ASSERT_EQ (r0, r1);
+  r0.set_nan (fp_prop::NO);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::YES);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::VARYING);
+  ASSERT_EQ (r0, r1);
+
+  // NAN ranges are not equal to each other.
+  r0 = frange_nan ();
+  r1 = r0;
+  ASSERT_FALSE (r0 == r1);
+  ASSERT_FALSE (r0 == r0);
+  ASSERT_TRUE (r0 != r0);
+
+  // Make sure that combining NAN and INF doesn't give any crazy results.
+  r0 = frange_nan ();
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  r1 = frange_float ("+Inf", "+Inf");
+  r0.union_ (r1);
+  // [INF, INF] U NAN = VARYING
+  ASSERT_TRUE (r0.varying_p ());
+
+  // [INF, INF] ^ NAN = VARYING
+  r0 = frange_nan ();
+  r1 = frange_float ("+Inf", "+Inf");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.varying_p ());
+
+  // NAN ^ NAN = NAN
+  r0 = frange_nan ();
+  r1 = frange_nan ();
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+
+  // VARYING ^ NAN = NAN.
+  r0 = frange_nan ();
+  r1.set_varying (float_type_node);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+}
+
+static void
+range_tests_signed_zeros ()
+{
+  tree zero = build_zero_cst (float_type_node);
+  tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero);
+  frange r0, r1;
+
+  // ?? If -0.0 == +0.0, then a range of [-0.0, -0.0] should
+  // contain +0.0 and vice versa.  We probably need a property to
+  // track signed zeros in the frange like we do for NAN, to
+  // properly model all this.
+  r0 = frange (zero, zero);
+  r1 = frange (neg_zero, neg_zero);
+  ASSERT_TRUE (r0.contains_p (zero));
+  ASSERT_TRUE (r0.contains_p (neg_zero));
+  ASSERT_TRUE (r1.contains_p (zero));
+  ASSERT_TRUE (r1.contains_p (neg_zero));
+}
+
+static void
+range_tests_floats ()
+{
+  frange r0, r1;
+
+  range_tests_nan ();
+
+  if (HONOR_SIGNED_ZEROS (float_type_node))
+    range_tests_signed_zeros ();
+
+  // A range of [-INF,+INF] is actually VARYING...
+  r0 = frange_float ("-Inf", "+Inf");
+  ASSERT_TRUE (r0.varying_p ());
+  // ...unless it has some special property...
+  r0.set_nan (fp_prop::NO);
+  ASSERT_FALSE (r0.varying_p ());
+
+  // The endpoints of a VARYING are +-INF.
+  REAL_VALUE_TYPE inf, ninf;
+  real_inf (&inf, 0);
+  real_inf (&ninf, 1);
+  r0.set_varying (float_type_node);
+  ASSERT_TRUE (real_identical (&r0.lower_bound (), &ninf));
+  ASSERT_TRUE (real_identical (&r0.upper_bound (), &inf));
+
+  // The maximum representable range for a type is still a subset of VARYING.
+  REAL_VALUE_TYPE q, r;
+  real_min_representable (&q, float_type_node);
+  real_max_representable (&r, float_type_node);
+  r0 = frange (float_type_node, q, r);
+  // r0 is not a varying, because it does not include -INF/+INF.
+  ASSERT_FALSE (r0.varying_p ());
+  // The upper bound of r0 must be less than +INF.
+  ASSERT_TRUE (real_less (&r0.upper_bound (), &inf));
+  // The lower bound of r0 must be greater than -INF.
+  ASSERT_TRUE (real_less (&ninf, &r0.lower_bound ()));
+
+  // For most architectures, where float and double are different
+  // sizes, having the same endpoints does not necessarily mean the
+  // ranges are equal.
+  if (!types_compatible_p (float_type_node, double_type_node))
+    {
+      r0 = frange_float ("3.0", "3.0", float_type_node);
+      r1 = frange_float ("3.0", "3.0", double_type_node);
+      ASSERT_NE (r0, r1);
+    }
+
+  // [3,5] U [10,12] = [3,12].
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("10", "12");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "12"));
+
+  // [5,10] U [4,8] = [4,10]
+  r0 = frange_float ("5", "10");
+  r1 = frange_float ("4", "8");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "10"));
+
+  // [3,5] U [4,10] = [3,10]
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("4", "10");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "10"));
+
+  // [4,10] U [5,11] = [4,11]
+  r0 = frange_float ("4", "10");
+  r1 = frange_float ("5", "11");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "11"));
+
+  // [3,12] ^ [10,12] = [10,12].
+  r0 = frange_float ("3", "12");
+  r1 = frange_float ("10", "12");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "12"));
+
+  // [10,12] ^ [11,11] = [11,11]
+  r0 = frange_float ("10", "12");
+  r1 = frange_float ("11", "11");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("11", "11"));
+
+  // [10,20] ^ [5,15] = [10,15]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("5",  "15");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "15"));
+
+  // [10,20] ^ [15,25] = [15,20]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("15", "25");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("15", "20"));
+
+  // [10,20] ^ [21,25] = []
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("21", "25");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+}
+
 void
 range_tests ()
 {
@@ -3312,6 +3683,7 @@ range_tests ()
   range_tests_int_range_max ();
   range_tests_strict_enum ();
   range_tests_nonzero_bits ();
+  range_tests_floats ();
   range_tests_misc ();
 }
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index f0075d0fb1a888ea0a77b0ee7b734cc945ab90fd..43b231b8fe0b8e12f7824d9b1938f3baebd5e890 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -314,14 +314,10 @@ public:
   bool intersect (const frange_props &other);
   bool operator== (const frange_props &other) const;
   FP_PROP_ACCESSOR(nan)
-  FP_PROP_ACCESSOR(inf)
-  FP_PROP_ACCESSOR(ninf)
 private:
   union {
     struct {
       unsigned char nan : 2;
-      unsigned char inf : 2;
-      unsigned char ninf : 2;
     } bits;
     unsigned char bytes;
   } u;
@@ -345,34 +341,62 @@ class frange : public vrange
 public:
   frange ();
   frange (const frange &);
+  frange (tree, tree, value_range_kind = VR_RANGE);
+  frange (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	  value_range_kind = VR_RANGE);
   static bool supports_p (const_tree type)
   {
     return SCALAR_FLOAT_TYPE_P (type);
   }
   virtual tree type () const override;
   virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+  void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
+	    value_range_kind = VR_RANGE);
   virtual void set_varying (tree type) override;
   virtual void set_undefined () override;
   virtual bool union_ (const vrange &) override;
   virtual bool intersect (const vrange &) override;
+  virtual bool contains_p (tree) const override;
+  virtual bool singleton_p (tree *result = NULL) const override;
   virtual bool supports_type_p (const_tree type) const override;
   virtual void accept (const vrange_visitor &v) const override;
+  virtual bool zero_p () const;
+  virtual bool nonzero_p () const;
+  virtual void set_nonzero (tree type);
+  virtual void set_zero (tree type);
+  virtual void set_nonnegative (tree type);
   frange& operator= (const frange &);
   bool operator== (const frange &) const;
   bool operator!= (const frange &r) const { return !(*this == r); }
+  const REAL_VALUE_TYPE &lower_bound () const;
+  const REAL_VALUE_TYPE &upper_bound () const;
 
   // Each fp_prop can be accessed with get_PROP() and set_PROP().
   FRANGE_PROP_ACCESSOR(nan)
-  FRANGE_PROP_ACCESSOR(inf)
-  FRANGE_PROP_ACCESSOR(ninf)
 private:
   void verify_range ();
   bool normalize_kind ();
 
   frange_props m_props;
   tree m_type;
+  REAL_VALUE_TYPE m_min;
+  REAL_VALUE_TYPE m_max;
 };
 
+inline const REAL_VALUE_TYPE &
+frange::lower_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_min;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::upper_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_max;
+}
+
 // is_a<> and as_a<> implementation for vrange.
 
 // Anything we haven't specialized is a hard fail.
@@ -1050,10 +1074,9 @@ vrp_val_min (const_tree type)
     return build_zero_cst (const_cast<tree> (type));
   if (frange::supports_p (type))
     {
-      REAL_VALUE_TYPE real, real_ninf;
-      real_inf (&real);
-      real_ninf = real_value_negate (&real);
-      return build_real (const_cast <tree> (type), real_ninf);
+      REAL_VALUE_TYPE ninf;
+      real_inf (&ninf, 1);
+      return build_real (const_cast <tree> (type), ninf);
     }
   return NULL_TREE;
 }
@@ -1096,6 +1119,26 @@ frange::frange (const frange &src)
   *this = src;
 }
 
+// frange constructor from REAL_VALUE_TYPE endpoints.
+
+inline
+frange::frange (tree type,
+		const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+		value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (type, min, max, kind);
+}
+
+// frange constructor from trees.
+
+inline
+frange::frange (tree min, tree max, value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (min, max, kind);
+}
+
 inline tree
 frange::type () const
 {
@@ -1107,6 +1150,8 @@ frange::set_varying (tree type)
 {
   m_kind = VR_VARYING;
   m_type = type;
+  real_inf (&m_min, 1);
+  real_inf (&m_max, 0);
   m_props.set_varying ();
 }
 
@@ -1116,6 +1161,29 @@ frange::set_undefined ()
   m_kind = VR_UNDEFINED;
   m_type = NULL;
   m_props.set_undefined ();
+  memset (&m_min, 0, sizeof (m_min));
+  memset (&m_max, 0, sizeof (m_max));
+}
+
+// Set R to maximum representable value for TYPE.
+
+inline void
+real_max_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  char buf[128];
+  get_max_float (REAL_MODE_FORMAT (TYPE_MODE (type)),
+		 buf, sizeof (buf), false);
+  int res = real_from_string (r, buf);
+  gcc_checking_assert (!res);
+}
+
+// Set R to minimum representable value for TYPE.
+
+inline void
+real_min_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  real_max_representable (r, type);
+  *r = real_value_negate (r);
 }
 
 #endif // GCC_VALUE_RANGE_H