diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index fbc14a730ad..270490010e2 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -218,7 +218,11 @@ frange_add_zeros (frange &r, tree type)
 
   if (HONOR_SIGNED_ZEROS (type)
       && (real_iszero (&r.lower_bound ()) || real_iszero (&r.upper_bound ())))
-    r.set_signbit (fp_prop::VARYING);
+    {
+      frange zero;
+      zero.set_zero (type);
+      r.union_ (zero);
+    }
 }
 
 // Build a range that is <= VAL and store it in R.
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index ea6e4b979ad..0bdd670982b 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -219,17 +219,8 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt)
       {
 	frange &f = as_a <frange> (r);
 	f.set (expr, expr);
-
-	// Singletons from the tree world have known properties.
-	REAL_VALUE_TYPE *rv = TREE_REAL_CST_PTR (expr);
-	if (real_isnan (rv))
-	  f.update_nan (fp_prop::YES);
-	else
+	if (!real_isnan (TREE_REAL_CST_PTR (expr)))
 	  f.clear_nan ();
-	if (real_isneg (rv))
-	  f.set_signbit (fp_prop::YES);
-	else
-	  f.set_signbit (fp_prop::NO);
 	return true;
       }
 
diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index b124e46cb9e..49b16d6a5b1 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -134,34 +134,39 @@ vrange_printer::visit (const frange &r) const
   if (r.varying_p ())
     {
       pp_string (pp, "VARYING");
+      print_frange_nan (r);
       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 ("SIGN", r.get_signbit ());
+  bool has_endpoints = !r.known_nan ();
+  if (has_endpoints)
+    {
+      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_character (pp, ']');
+  print_frange_nan (r);
 }
 
-// Print the FP properties in an frange.
+// Print the NAN info for an frange.
 
 void
-vrange_printer::print_frange_prop (const char *str, const fp_prop &prop) const
+vrange_printer::print_frange_nan (const frange &r) const
 {
-  if (prop.varying_p ())
-    return;
-
-  if (prop.yes_p ())
-    pp_string (pp, str);
-  else if (prop.no_p ())
+  if (r.maybe_nan ())
     {
-      pp_character (pp, '!');
-      pp_string (pp, str);
+      if (r.m_pos_nan && r.m_neg_nan)
+	{
+	  pp_string (pp, " NAN");
+	  return;
+	}
+      bool nan_sign = r.m_neg_nan;
+      if (nan_sign)
+	pp_string (pp, " -NAN");
+      else
+	pp_string (pp, " +NAN");
     }
-  pp_character (pp, ' ');
 }
diff --git a/gcc/value-range-pretty-print.h b/gcc/value-range-pretty-print.h
index ad06c93c044..20c26598fe7 100644
--- a/gcc/value-range-pretty-print.h
+++ b/gcc/value-range-pretty-print.h
@@ -31,7 +31,7 @@ public:
 private:
   void print_irange_bound (const wide_int &w, tree type) const;
   void print_irange_bitmasks (const irange &) const;
-  void print_frange_prop (const char *str, const fp_prop &) const;
+  void print_frange_nan (const frange &) const;
 
   pretty_printer *pp;
 };
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index b7a23fa9825..de7575ed48d 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -253,9 +253,11 @@ frange_storage_slot::set_frange (const frange &r)
   gcc_checking_assert (fits_p (r));
   gcc_checking_assert (!r.undefined_p ());
 
+  m_kind = r.m_kind;
   m_min = r.m_min;
   m_max = r.m_max;
-  m_props = r.m_props;
+  m_pos_nan = r.m_pos_nan;
+  m_neg_nan = r.m_neg_nan;
 }
 
 void
@@ -264,11 +266,12 @@ frange_storage_slot::get_frange (frange &r, tree type) const
   gcc_checking_assert (r.supports_type_p (type));
 
   r.set_undefined ();
-  r.m_kind = VR_RANGE;
-  r.m_props = m_props;
+  r.m_kind = m_kind;
   r.m_type = type;
   r.m_min = m_min;
   r.m_max = m_max;
+  r.m_pos_nan = m_pos_nan;
+  r.m_neg_nan = m_neg_nan;
   r.normalize_kind ();
 
   if (flag_checking)
diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h
index f506789f3d1..0cf95ebf7c1 100644
--- a/gcc/value-range-storage.h
+++ b/gcc/value-range-storage.h
@@ -113,12 +113,11 @@ class GTY (()) frange_storage_slot
   frange_storage_slot (const frange &r) { set_frange (r); }
   DISABLE_COPY_AND_ASSIGN (frange_storage_slot);
 
-  // We can get away with just storing the properties and the
-  // endpoints because the type can be gotten from the SSA, and
-  // UNDEFINED is unsupported, so it can only be a VR_RANGE.
+  enum value_range_kind m_kind;
   REAL_VALUE_TYPE m_min;
   REAL_VALUE_TYPE m_max;
-  frange_props m_props;
+  bool m_pos_nan;
+  bool m_neg_nan;
 };
 
 class obstack_vrange_allocator final: public vrange_allocator
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index d759fcf178c..1c6061649b5 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -267,91 +267,6 @@ tree_compare (tree_code code, tree op1, tree op2)
   return !integer_zerop (fold_build2 (code, integer_type_node, op1, op2));
 }
 
-// Set the NAN property.  Adjust the range if appopriate.
-
-void
-frange::update_nan (fp_prop::kind k)
-{
-  if (k == fp_prop::YES)
-    {
-      if (!maybe_nan ())
-	{
-	  set_undefined ();
-	  return;
-	}
-      gcc_checking_assert (!undefined_p ());
-      set_nan (m_type);
-      return;
-    }
-
-  if (k == fp_prop::NO && known_nan ())
-    {
-      set_undefined ();
-      return;
-    }
-
-  // Setting VARYING on an obviously NAN range is a no-op.
-  if (k == fp_prop::VARYING && real_isnan (&m_min))
-    return;
-
-  m_props.set_nan (k);
-  normalize_kind ();
-  if (flag_checking)
-    verify_range ();
-}
-
-// Set the SIGNBIT property.  Adjust the range if appropriate.
-
-void
-frange::set_signbit (fp_prop::kind k)
-{
-  gcc_checking_assert (m_type);
-
-  // No additional adjustments are needed for a NAN.
-  if (known_nan ())
-    {
-      m_props.set_signbit (k);
-      return;
-    }
-  // Ignore sign changes when they're set correctly.
-  if (!maybe_nan ())
-    {
-      // It's negative and we're trying to make it negative or varying.
-      if (real_less (&m_max, &dconst0) && (k == fp_prop::YES
-					   || k == fp_prop::VARYING))
-	return;
-      // It's positive and we're trying to make it positive or varying.
-      if (real_less (&dconst0, &m_min) && (k == fp_prop::NO
-					   || k == fp_prop::VARYING))
-	return;
-    }
-  // Adjust the range depending on the sign bit.
-  if (k == fp_prop::YES)
-    {
-      // Crop the range to [-INF, 0].
-      frange crop (m_type, dconstninf, dconst0);
-      intersect (crop);
-      if (!undefined_p ())
-	m_props.set_signbit (fp_prop::YES);
-    }
-  else if (k == fp_prop::NO)
-    {
-      // Crop the range to [0, +INF].
-      frange crop (m_type, dconst0, dconstinf);
-      intersect (crop);
-      if (!undefined_p ())
-	m_props.set_signbit (fp_prop::NO);
-    }
-  else
-    {
-      m_props.set_signbit (fp_prop::VARYING);
-      normalize_kind ();
-    }
-
-  if (flag_checking)
-    verify_range ();
-}
-
 // Setter for franges.
 
 void
@@ -375,24 +290,23 @@ frange::set (tree min, tree max, value_range_kind kind)
       gcc_checking_assert (real_identical (TREE_REAL_CST_PTR (min),
 					   TREE_REAL_CST_PTR (max)));
       tree type = TREE_TYPE (min);
-      set_nan (type);
+      bool sign = real_isneg (TREE_REAL_CST_PTR (min));
+      set_nan (type, sign);
       return;
     }
 
   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);
-
-  // Set SIGNBIT property for positive and negative ranges.
-  if (real_less (&m_max, &dconst0))
-    m_props.signbit_set_yes ();
-  else if (real_less (&dconst0, &m_min))
-    m_props.signbit_set_no ();
+  m_pos_nan = true;
+  m_neg_nan = true;
 
   if (!HONOR_NANS (m_type))
-    m_props.nan_set_no ();
+    {
+      m_pos_nan = false;
+      m_neg_nan = false;
+    }
 
   // Check for swapped ranges.
   gcc_checking_assert (tree_compare (LE_EXPR, min, max));
@@ -423,18 +337,11 @@ frange::set (tree type,
 bool
 frange::normalize_kind ()
 {
-  // Undefined is viral.
-  if (m_props.nan_undefined_p () || m_props.signbit_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 ())
+      if (m_pos_nan && m_neg_nan)
 	{
 	  set_varying (m_type);
 	  return true;
@@ -442,8 +349,7 @@ frange::normalize_kind ()
     }
   else if (m_kind == VR_VARYING)
     {
-      // If a VARYING has any FP properties, it's no longer VARYING.
-      if (!m_props.varying_p ())
+      if (!m_pos_nan || !m_neg_nan)
 	{
 	  m_kind = VR_RANGE;
 	  m_min = dconstninf;
@@ -454,6 +360,60 @@ frange::normalize_kind ()
   return false;
 }
 
+// Union or intersect the zero endpoints of two ranges.  For example:
+//   [-0,  x] U [+0,  x] => [-0,  x]
+//   [ x, -0] U [ x, +0] => [ x, +0]
+//   [-0,  x] ^ [+0,  x] => [+0,  x]
+//   [ x, -0] ^ [ x, +0] => [ x, -0]
+//
+// UNION_P is true when performing a union, or false when intersecting.
+
+bool
+frange::combine_zeros (const frange &r, bool union_p)
+{
+  bool changed = false;
+  if (real_iszero (&m_min) && real_iszero (&r.m_min)
+      && real_isneg (&m_min) != real_isneg (&r.m_min))
+    {
+      m_min.sign = union_p;
+      changed = true;
+    }
+  if (real_iszero (&m_max) && real_iszero (&r.m_max)
+      && real_isneg (&m_max) != real_isneg (&r.m_max))
+    {
+      m_max.sign = !union_p;
+      changed = true;
+    }
+  // If the signs are swapped, the resulting range is empty.
+  if (m_min.sign == 0 && m_max.sign == 1)
+    {
+      m_kind = VR_UNDEFINED;
+      changed = true;
+    }
+  return changed;
+}
+
+// Union two ranges when one is known to be a NAN.
+
+bool
+frange::union_nans (const frange &r)
+{
+  gcc_checking_assert (known_nan () || r.known_nan ());
+
+  if (known_nan ())
+    {
+      m_kind = r.m_kind;
+      m_min = r.m_min;
+      m_max = r.m_max;
+    }
+  m_pos_nan |= r.m_pos_nan;
+  m_neg_nan |= r.m_neg_nan;
+  normalize_kind ();
+  if (flag_checking)
+    verify_range ();
+  return true;
+}
+
 bool
 frange::union_ (const vrange &v)
 {
@@ -467,29 +427,18 @@ frange::union_ (const vrange &v)
       return true;
     }
 
-  // If one side has a NAN, the union is the other side, plus the union
-  // of the properties and the possibility of a NAN.
-  if (known_nan ())
-    {
-      frange_props save = m_props;
-      *this = r;
-      m_props = save;
-      m_props.union_ (r.m_props);
-      update_nan (fp_prop::VARYING);
-      if (flag_checking)
-	verify_range ();
-      return true;
-    }
-  if (r.known_nan ())
+  // Combine NAN info.
+  if (known_nan () || r.known_nan ())
+    return union_nans (r);
+  bool changed = false;
+  if (m_pos_nan != r.m_pos_nan || m_neg_nan != r.m_neg_nan)
     {
-      m_props.union_ (r.m_props);
-      update_nan (fp_prop::VARYING);
-      if (flag_checking)
-	verify_range ();
-      return true;
+      m_pos_nan |= r.m_pos_nan;
+      m_neg_nan |= r.m_neg_nan;
+      changed = true;
     }
 
-  bool changed = m_props.union_ (r.m_props);
+  // Combine endpoints.
   if (real_less (&r.m_min, &m_min))
     {
       m_min = r.m_min;
@@ -500,13 +449,38 @@ frange::union_ (const vrange &v)
       m_max = r.m_max;
       changed = true;
     }
-  changed |= normalize_kind ();
 
+  if (HONOR_SIGNED_ZEROS (m_type))
+    changed |= combine_zeros (r, true);
+
+  changed |= normalize_kind ();
   if (flag_checking)
     verify_range ();
   return changed;
 }
 
+// Intersect two ranges when one is known to be a NAN.
+
+bool
+frange::intersect_nans (const frange &r)
+{
+  gcc_checking_assert (known_nan () || r.known_nan ());
+
+  m_kind = VR_UNDEFINED;
+  m_pos_nan &= r.m_pos_nan;
+  m_neg_nan &= r.m_neg_nan;
+  if (!maybe_nan ())
+    {
+      // If the NAN was intersected out, the resulting range is empty.
+      set_undefined ();
+      return true;
+    }
+  normalize_kind ();
+  if (flag_checking)
+    verify_range ();
+  return true;
+}
+
 bool
 frange::intersect (const vrange &v)
 {
@@ -525,25 +499,18 @@ frange::intersect (const vrange &v)
       return true;
     }
 
-  // If two NANs are not exactly the same, drop to an unknown NAN,
-  // otherwise there's nothing to do.
-  if (known_nan () && r.known_nan ())
-    {
-      if (m_props == r.m_props)
-	return false;
-
-      set_nan (m_type);
-      return true;
-    }
-  // ?? Perhaps the intersection of a NAN and anything is a NAN ??.
+  // Combine NAN info.
   if (known_nan () || r.known_nan ())
+    return intersect_nans (r);
+  bool changed = false;
+  if (m_pos_nan != r.m_pos_nan || m_neg_nan != r.m_neg_nan)
     {
-      set_varying (m_type);
-      return true;
+      m_pos_nan &= r.m_pos_nan;
+      m_neg_nan &= r.m_neg_nan;
+      changed = true;
     }
 
-  bool changed = m_props.intersect (r.m_props);
-
+  // Combine endpoints.
   if (real_less (&m_min, &r.m_min))
     {
       m_min = r.m_min;
@@ -554,14 +521,25 @@ frange::intersect (const vrange &v)
       m_max = r.m_max;
       changed = true;
     }
-  // If the endpoints are swapped, the ranges are disjoint.
   if (real_less (&m_max, &m_min))
     {
+      // If the endpoints are swapped, the resulting range is empty.
+      if (maybe_nan ())
+	{
+	  // An empty range with a NAN is just a NAN.
+	  m_kind = VR_UNDEFINED;
+	  if (flag_checking)
+	    verify_range ();
+	  return true;
+	}
       set_undefined ();
       return true;
     }
-  changed |= normalize_kind ();
 
+  if (HONOR_SIGNED_ZEROS (m_type))
+    changed |= combine_zeros (r, false);
+
+  changed |= normalize_kind ();
   if (flag_checking)
     verify_range ();
   return changed;
@@ -574,7 +552,8 @@ frange::operator= (const frange &src)
   m_type = src.m_type;
   m_min = src.m_min;
   m_max = src.m_max;
-  m_props = src.m_props;
+  m_pos_nan = src.m_pos_nan;
+  m_neg_nan = src.m_neg_nan;
 
   if (flag_checking)
     verify_range ();
@@ -597,7 +576,8 @@ frange::operator== (const frange &src) const
 
       return (real_identical (&m_min, &src.m_min)
 	      && real_identical (&m_max, &src.m_max)
-	      && m_props == src.m_props
+	      && m_pos_nan == src.m_pos_nan
+	      && m_neg_nan == src.m_neg_nan
 	      && types_compatible_p (m_type, src.m_type));
     }
   return false;
@@ -617,21 +597,24 @@ frange::contains_p (tree cst) const
   if (varying_p ())
     return true;
 
+  if (real_isnan (rv))
+    {
+      // No NAN in range.
+      if (!m_pos_nan && !m_neg_nan)
+	return false;
+      // Both +NAN and -NAN are present.
+      if (m_pos_nan && m_neg_nan)
+	return true;
+      return m_neg_nan == rv->sign;
+    }
+  if (known_nan ())
+    return false;
 
   if (real_compare (GE_EXPR, rv, &m_min) && real_compare (LE_EXPR, rv, &m_max))
     {
+      // Make sure the signs are equal for signed zeros.
       if (HONOR_SIGNED_ZEROS (m_type) && real_iszero (rv))
-	{
-	  // FIXME: This is still using get_signbit() instead of
-	  // known_signbit() because the latter bails on possible NANs
-	  // (for now).
-	  if (get_signbit ().yes_p ())
-	    return real_isneg (rv);
-	  else if (get_signbit ().no_p ())
-	    return !real_isneg (rv);
-	  else
-	    return true;
-	}
+	return m_min.sign == m_max.sign && m_min.sign == rv->sign;
       return true;
     }
   return false;
@@ -651,26 +634,6 @@ frange::singleton_p (tree *result) const
       if (HONOR_NANS (m_type) && maybe_nan ())
 	return false;
 
-      // Return the appropriate zero if known.
-      if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
-	{
-	  bool signbit;
-	  if (known_signbit (signbit))
-	    {
-	      if (signbit)
-		{
-		  if (result)
-		    *result = build_real (m_type, real_value_negate (&dconst0));
-		}
-	      else
-		{
-		  if (result)
-		    *result = build_real (m_type, dconst0);
-		}
-	      return true;
-	    }
-	  return false;
-	}
       if (result)
 	*result = build_real (m_type, m_min);
       return true;
@@ -687,57 +650,31 @@ frange::supports_type_p (const_tree type) const
 void
 frange::verify_range ()
 {
-  if (undefined_p ())
-    {
-      gcc_checking_assert (m_props.undefined_p ());
-      return;
-    }
-  gcc_checking_assert (!m_props.undefined_p ());
-
+  if (m_kind == VR_UNDEFINED)
+    return;
   if (varying_p ())
     {
-      gcc_checking_assert (m_props.varying_p ());
+      gcc_checking_assert (m_pos_nan && m_neg_nan);
+      gcc_checking_assert (real_isinf (&m_min, 1));
+      gcc_checking_assert (real_isinf (&m_max, 0));
       return;
     }
 
+  // NANs cannot appear in the endpoints of a range.
+  gcc_checking_assert (!real_isnan (&m_min) && !real_isnan (&m_max));
+
   // We don't support the inverse of an frange (yet).
   gcc_checking_assert (m_kind == VR_RANGE);
 
-  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 (known_nan ());
-    }
-  else
-    // Make sure we don't have swapped ranges.
-    gcc_checking_assert (!real_less (&m_max, &m_min));
+  // 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 (known_nan ())
-    {
-      gcc_checking_assert (real_isnan (&m_min));
-      gcc_checking_assert (real_isnan (&m_max));
-    }
-  else
-    {
-      // Make sure the signbit and range agree.
-      bool signbit;
-      if (known_signbit (signbit))
-	{
-	  if (signbit)
-	    gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0));
-	  else
-	    gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0));
-	}
-    }
+  // [ +0.0, -0.0 ] is nonsensical.
+  gcc_checking_assert (!(real_iszero (&m_min, 0) && real_iszero (&m_max, 1)));
 
   // If all the properties are clear, we better not span the entire
   // domain, because that would make us varying.
-  if (m_props.varying_p ())
+  if (m_pos_nan && m_neg_nan)
     gcc_checking_assert (!real_isinf (&m_min, 1) || !real_isinf (&m_max, 0));
 }
 
@@ -755,16 +692,24 @@ frange::nonzero_p () const
   return false;
 }
 
-// Set range to [+0.0, +0.0].
+// Set range to [+0.0, +0.0] if honoring signed zeros, or [0.0, 0.0]
+// otherwise.
 
 void
 frange::set_zero (tree type)
 {
-  tree zero = build_zero_cst (type);
-  set (zero, zero);
+  if (HONOR_SIGNED_ZEROS (type))
+    {
+      REAL_VALUE_TYPE dconstm0 = dconst0;
+      dconstm0.sign = 1;
+      set (type, dconstm0, dconst0);
+      clear_nan ();
+    }
+  else
+    set (type, dconst0, dconst0);
 }
 
-// Return TRUE for any [0.0, 0.0] regardless of sign.
+// Return TRUE for any zero regardless of sign.
 
 bool
 frange::zero_p () const
@@ -777,9 +722,7 @@ frange::zero_p () const
 void
 frange::set_nonnegative (tree type)
 {
-  tree zero = build_zero_cst (type);
-  tree inf = vrp_val_max (type);
-  set (zero, inf);
+  set (type, dconst0, dconstinf);
 }
 
 // Here we copy between any two irange's.  The ranges can be legacy or
@@ -3637,8 +3580,21 @@ range_tests_nan ()
       ASSERT_EQ (r0, r1);
       r0.clear_nan ();
       ASSERT_NE (r0, r1);
+      r0.update_nan ();
+      ASSERT_EQ (r0, r1);
+
+      // [10, 20] NAN ^ [30, 40] NAN = NAN.
+      r0 = frange_float ("10", "20");
+      r1 = frange_float ("30", "40");
+      r0.intersect (r1);
+      ASSERT_TRUE (r0.known_nan ());
+
+      // [3,5] U [5,10] NAN = ... NAN
+      r0 = frange_float ("3", "5");
       r0.clear_nan ();
-      ASSERT_NE (r0, r1);
+      r1 = frange_float ("5", "10");
+      r0.union_ (r1);
+      ASSERT_TRUE (r0.maybe_nan ());
     }
 
   // NAN ranges are not equal to each other.
@@ -3663,15 +3619,15 @@ range_tests_nan ()
   r0.set_nan (float_type_node);
   r1.set_nan (float_type_node);
   r0.union_ (r1);
-  ASSERT_TRUE (real_isnan (&r0.lower_bound ()));
-  ASSERT_TRUE (real_isnan (&r1.upper_bound ()));
   ASSERT_TRUE (r0.known_nan ());
 
-  // [INF, INF] ^ NAN = VARYING
+  // [INF, INF] NAN ^ NAN = NAN
   r0.set_nan (float_type_node);
   r1 = frange_float ("+Inf", "+Inf");
+  if (!HONOR_NANS (float_type_node))
+    r1.update_nan ();
   r0.intersect (r1);
-  ASSERT_TRUE (r0.varying_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // NAN ^ NAN = NAN
   r0.set_nan (float_type_node);
@@ -3679,18 +3635,48 @@ range_tests_nan ()
   r0.intersect (r1);
   ASSERT_TRUE (r0.known_nan ());
 
+  // +NAN ^ -NAN = UNDEFINED
+  r0.set_nan (float_type_node, false);
+  r1.set_nan (float_type_node, true);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+
   // VARYING ^ NAN = NAN.
   r0.set_nan (float_type_node);
   r1.set_varying (float_type_node);
   r0.intersect (r1);
   ASSERT_TRUE (r0.known_nan ());
 
-  // Setting the NAN bit to yes, forces to range to [NAN, NAN].
+  // [3,4] ^ NAN = UNDEFINED.
+  r0 = frange_float ("3", "4");
+  r0.clear_nan ();
+  r1.set_nan (float_type_node);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+
+  // [-3, 5] ^ NAN = UNDEFINED
+  r0 = frange_float ("-3", "5");
+  r0.clear_nan ();
+  r1.set_nan (float_type_node);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+
+  // Setting the NAN bit to yes does not make us a known NAN.
   r0.set_varying (float_type_node);
-  r0.update_nan (fp_prop::YES);
-  ASSERT_TRUE (r0.known_nan ());
-  ASSERT_TRUE (real_isnan (&r0.lower_bound ()));
-  ASSERT_TRUE (real_isnan (&r0.upper_bound ()));
+  r0.update_nan ();
+  ASSERT_FALSE (r0.known_nan ());
+
+  // NAN is in a VARYING.
+  r0.set_varying (float_type_node);
+  real_nan (&r, "", 1, TYPE_MODE (float_type_node));
+  tree nan = build_real (float_type_node, r);
+  ASSERT_TRUE (r0.contains_p (nan));
+
+  // -NAN is in a VARYING.
+  r0.set_varying (float_type_node);
+  q = real_value_negate (&r);
+  tree neg_nan = build_real (float_type_node, q);
+  ASSERT_TRUE (r0.contains_p (neg_nan));
 }
 
 static void
@@ -3702,49 +3688,84 @@ range_tests_signed_zeros ()
   frange r0, r1;
   bool signbit;
 
-  // Since -0.0 == +0.0, a range of [-0.0, -0.0] should contain +0.0
-  // and vice versa.
+  // [0,0] contains [0,0] but not [-0,-0] and vice versa.
   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 (!r0.contains_p (neg_zero));
   ASSERT_TRUE (r1.contains_p (neg_zero));
+  ASSERT_TRUE (!r1.contains_p (zero));
 
   // Test contains_p() when we know the sign of the zero.
-  r0 = frange(zero, zero);
-  r0.set_signbit (fp_prop::NO);
+  r0 = frange (zero, zero);
   ASSERT_TRUE (r0.contains_p (zero));
   ASSERT_FALSE (r0.contains_p (neg_zero));
-  r0.set_signbit (fp_prop::YES);
+  r0 = frange (neg_zero, neg_zero);
   ASSERT_TRUE (r0.contains_p (neg_zero));
   ASSERT_FALSE (r0.contains_p (zero));
 
-  // The intersection of zeros that differ in sign is the empty set.
-  r0 = frange (zero, zero);
-  r0.set_signbit (fp_prop::YES);
+  // The intersection of zeros that differ in sign is a NAN (or
+  // undefined if not honoring NANs).
+  r0 = frange (neg_zero, neg_zero);
   r1 = frange (zero, zero);
-  r1.set_signbit (fp_prop::NO);
   r0.intersect (r1);
-  ASSERT_TRUE (r0.undefined_p ());
+  if (HONOR_NANS (float_type_node))
+    ASSERT_TRUE (r0.known_nan ());
+  else
+    ASSERT_TRUE (r0.undefined_p ());
 
   // The union of zeros that differ in sign is a zero with unknown sign.
   r0 = frange (zero, zero);
-  r0.set_signbit (fp_prop::NO);
-  r1 = frange (zero, zero);
-  r1.set_signbit (fp_prop::YES);
+  r1 = frange (neg_zero, neg_zero);
   r0.union_ (r1);
   ASSERT_TRUE (r0.zero_p () && !r0.known_signbit (signbit));
 
-  // NAN U [5,6] should be [5,6] with no sign info.
+  // [-0, +0] has an unknown sign.
+  r0 = frange (neg_zero, zero);
+  ASSERT_TRUE (r0.zero_p () && !r0.known_signbit (signbit));
+
+  // [-0, +0] ^ [0, 0] is [0, 0]
+  r0 = frange (neg_zero, zero);
+  r1 = frange (zero, zero);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.zero_p ());
+
+  // NAN U [5,6] should be [5,6] NAN.
   r0.set_nan (float_type_node);
   r1 = frange_float ("5", "6");
+  r1.clear_nan ();
   r0.union_ (r1);
   real_from_string (&q, "5");
   real_from_string (&r, "6");
   ASSERT_TRUE (real_identical (&q, &r0.lower_bound ()));
   ASSERT_TRUE (real_identical (&r, &r0.upper_bound ()));
   ASSERT_TRUE (!r0.known_signbit (signbit));
+  ASSERT_TRUE (r0.maybe_nan ());
+
+  r0 = frange_float ("+0", "5");
+  r0.clear_nan ();
+  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
+
+  r0 = frange_float ("-0", "5");
+  r0.clear_nan ();
+  ASSERT_TRUE (!r0.known_signbit (signbit));
+
+  r0 = frange_float ("-0", "10");
+  r1 = frange_float ("0", "5");
+  r0.intersect (r1);
+  ASSERT_TRUE (real_iszero (&r0.lower_bound (), false));
+
+  r0 = frange_float ("-0", "5");
+  r1 = frange_float ("0", "5");
+  r0.union_ (r1);
+  ASSERT_TRUE (real_iszero (&r0.lower_bound (), true));
+
+  r0 = frange_float ("-5", "-0");
+  r0.update_nan ();
+  r1 = frange_float ("0", "0");
+  r1.update_nan ();
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.known_nan ());
 }
 
 static void
@@ -3753,22 +3774,6 @@ range_tests_signbit ()
   frange r0, r1;
   bool signbit;
 
-  // Setting the signbit drops the range to [-INF, 0].
-  r0.set_varying (float_type_node);
-  r0.set_signbit (fp_prop::YES);
-  ASSERT_TRUE (real_isinf (&r0.lower_bound (), 1));
-  ASSERT_TRUE (real_iszero (&r0.upper_bound ()));
-
-  // Setting the signbit for [-5, 10] crops the range to [-5, 0] with
-  // the signbit property set.
-  r0 = frange_float ("-5", "10");
-  r0.set_signbit (fp_prop::YES);
-  r0.clear_nan ();
-  ASSERT_TRUE (r0.known_signbit (signbit) && signbit);
-  r1 = frange_float ("-5", "0");
-  ASSERT_TRUE (real_identical (&r0.lower_bound (), &r1.lower_bound ()));
-  ASSERT_TRUE (real_identical (&r0.upper_bound (), &r1.upper_bound ()));
-
   // Negative numbers should have the SIGNBIT set.
   r0 = frange_float ("-5", "-1");
   r0.clear_nan ();
@@ -3780,7 +3785,7 @@ range_tests_signbit ()
   // Numbers containing zero should have an unknown SIGNBIT.
   r0 = frange_float ("0", "10");
   r0.clear_nan ();
-  ASSERT_TRUE (!r0.known_signbit (signbit));
+  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
   // Numbers spanning both positive and negative should have an
   // unknown SIGNBIT.
   r0 = frange_float ("-10", "10");
@@ -3788,17 +3793,6 @@ range_tests_signbit ()
   ASSERT_TRUE (!r0.known_signbit (signbit));
   r0.set_varying (float_type_node);
   ASSERT_TRUE (!r0.known_signbit (signbit));
-
-  // Ignore signbit changes when the sign bit is obviously known from
-  // the range.
-  r0 = frange_float ("5", "10");
-  r0.clear_nan ();
-  r0.set_signbit (fp_prop::VARYING);
-  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
-  r0 = frange_float ("-5", "-1");
-  r0.set_signbit (fp_prop::NO);
-  r0.clear_nan ();
-  ASSERT_TRUE (r0.undefined_p ());
 }
 
 static void
@@ -3896,9 +3890,19 @@ range_tests_floats ()
   r0.intersect (r1);
   ASSERT_EQ (r0, frange_float ("15", "20"));
 
+  // [10,20] NAN ^ [21,25] NAN = [NAN]
+  r0 = frange_float ("10", "20");
+  r0.update_nan ();
+  r1 = frange_float ("21", "25");
+  r1.update_nan ();
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.known_nan ());
+
   // [10,20] ^ [21,25] = []
   r0 = frange_float ("10", "20");
+  r0.clear_nan ();
   r1 = frange_float ("21", "25");
+  r1.clear_nan ();
   r0.intersect (r1);
   ASSERT_TRUE (r0.undefined_p ());
 }
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 4392de84c8b..cbb6496f976 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -93,7 +93,7 @@ public:
   virtual bool fits_p (const vrange &r) const;
 
   bool varying_p () const;
-  bool undefined_p () const;
+  virtual bool undefined_p () const;
   vrange& operator= (const vrange &);
   bool operator== (const vrange &) const;
   bool operator!= (const vrange &r) const { return !(*this == r); }
@@ -263,68 +263,6 @@ public:
   virtual void accept (const vrange_visitor &v) const override;
 };
 
-// Floating point property to represent possible values of a NAN, INF, etc.
-
-class fp_prop
-{
-public:
-  enum kind {
-    UNDEFINED	= 0x0,		// Prop is impossible.
-    YES		= 0x1,		// Prop is definitely set.
-    NO		= 0x2,		// Prop is definitely not set.
-    VARYING	= (YES | NO)	// Prop may hold.
-  };
-  fp_prop (kind f) : m_kind (f) { }
-  bool varying_p () const { return m_kind == VARYING; }
-  bool undefined_p () const { return m_kind == UNDEFINED; }
-  bool yes_p () const { return m_kind == YES; }
-  bool no_p () const { return m_kind == NO; }
-private:
-  unsigned char m_kind : 2;
-};
-
-// Accessors for individual FP properties.
-
-#define FP_PROP_ACCESSOR(NAME) \
-  void NAME##_set_varying () { u.bits.NAME = fp_prop::VARYING; }	\
-  void NAME##_set_yes () { u.bits.NAME = fp_prop::YES; }	\
-  void NAME##_set_no () { u.bits.NAME = fp_prop::NO; }	\
-  bool NAME##_varying_p () const { return u.bits.NAME == fp_prop::VARYING; } \
-  bool NAME##_undefined_p () const { return u.bits.NAME == fp_prop::UNDEFINED; } \
-  bool NAME##_yes_p () const { return u.bits.NAME == fp_prop::YES; }	\
-  bool NAME##_no_p () const { return u.bits.NAME == fp_prop::NO; } \
-  fp_prop get_##NAME () const				   \
-  { return fp_prop ((fp_prop::kind) u.bits.NAME); } \
-  void set_##NAME (fp_prop::kind f) { u.bits.NAME = f; }
-
-// Aggregate of all the FP properties in an frange packed into one
-// structure to save space.  Using explicit fp_prop's in the frange,
-// would take one byte per property because of padding.  Instead, we
-// can save all properties into one byte.
-
-class frange_props
-{
-public:
-  frange_props () { set_varying (); }
-  void set_varying () { u.bytes = 0xff; }
-  void set_undefined () { u.bytes = 0; }
-  bool varying_p () { return u.bytes == 0xff; }
-  bool undefined_p () { return u.bytes == 0; }
-  bool union_ (const frange_props &other);
-  bool intersect (const frange_props &other);
-  bool operator== (const frange_props &other) const;
-  FP_PROP_ACCESSOR(nan)
-  FP_PROP_ACCESSOR(signbit)
-private:
-  union {
-    struct {
-      unsigned char nan : 2;
-      unsigned char signbit : 2;
-    } bits;
-    unsigned char bytes;
-  } u;
-};
-
 // A floating point range.
 
 class frange : public vrange
@@ -349,8 +287,10 @@ public:
   void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
 	    value_range_kind = VR_RANGE);
   void set_nan (tree type);
+  void set_nan (tree type, bool sign);
   virtual void set_varying (tree type) override;
   virtual void set_undefined () override;
+  virtual bool undefined_p () const final override;
   virtual bool union_ (const vrange &) override;
   virtual bool intersect (const vrange &) override;
   virtual bool contains_p (tree) const override;
@@ -376,33 +316,33 @@ public:
   bool known_nan () const;
   bool known_signbit (bool &signbit) const;
 
-  // Accessors for FP properties.
-  void update_nan (fp_prop::kind f);
-  void clear_nan () { update_nan (fp_prop::NO); }
-  void set_signbit (fp_prop::kind);
+  void update_nan ();
+  void clear_nan ();
 private:
-  fp_prop get_nan () const { return m_props.get_nan (); }
-  fp_prop get_signbit () const { return m_props.get_signbit (); }
   void verify_range ();
   bool normalize_kind ();
+  bool union_nans (const frange &);
+  bool intersect_nans (const frange &);
+  bool combine_zeros (const frange &, bool union_p);
 
-  frange_props m_props;
   tree m_type;
   REAL_VALUE_TYPE m_min;
   REAL_VALUE_TYPE m_max;
+  bool m_pos_nan;
+  bool m_neg_nan;
 };
 
 inline const REAL_VALUE_TYPE &
 frange::lower_bound () const
 {
-  gcc_checking_assert (!undefined_p ());
+  gcc_checking_assert (!undefined_p () && !known_nan ());
   return m_min;
 }
 
 inline const REAL_VALUE_TYPE &
 frange::upper_bound () const
 {
-  gcc_checking_assert (!undefined_p ());
+  gcc_checking_assert (!undefined_p () && !known_nan ());
   return m_max;
 }
 
@@ -1082,30 +1022,6 @@ vrp_val_min (const_tree type)
   return NULL_TREE;
 }
 
-// Supporting methods for frange.
-
-inline bool
-frange_props::operator== (const frange_props &other) const
-{
-  return u.bytes == other.u.bytes;
-}
-
-inline bool
-frange_props::union_ (const frange_props &other)
-{
-  unsigned char saved = u.bytes;
-  u.bytes |= other.u.bytes;
-  return u.bytes != saved;
-}
-
-inline bool
-frange_props::intersect (const frange_props &other)
-{
-  unsigned char saved = u.bytes;
-  u.bytes &= other.u.bytes;
-  return u.bytes != saved;
-}
-
 inline
 frange::frange ()
 {
@@ -1154,15 +1070,52 @@ frange::set_varying (tree type)
   m_type = type;
   m_min = dconstninf;
   m_max = dconstinf;
-  m_props.set_varying ();
+  m_pos_nan = true;
+  m_neg_nan = true;
 }
 
 inline void
 frange::set_undefined ()
 {
   m_kind = VR_UNDEFINED;
-  m_type = NULL;
-  m_props.set_undefined ();
+  m_pos_nan = false;
+  m_neg_nan = false;
+  if (flag_checking)
+    verify_range ();
+}
+
+// Set the NAN bit and adjust the range.
+
+inline void
+frange::update_nan ()
+{
+  gcc_checking_assert (!undefined_p ());
+  m_pos_nan = true;
+  m_neg_nan = true;
+  normalize_kind ();
+  if (flag_checking)
+    verify_range ();
+}
+
+// Clear the NAN bit and adjust the range.
+
+inline void
+frange::clear_nan ()
+{
+  gcc_checking_assert (!undefined_p ());
+  m_pos_nan = false;
+  m_neg_nan = false;
+  normalize_kind ();
+  if (flag_checking)
+    verify_range ();
+}
+
+// Return TRUE if range is the empty set.
+
+inline bool
+frange::undefined_p () const
+{
+  return m_kind == VR_UNDEFINED && !m_pos_nan && !m_neg_nan;
 }
 
 // Set R to maximum representable value for TYPE.
@@ -1186,19 +1139,28 @@ real_min_representable (REAL_VALUE_TYPE *r, tree type)
   *r = real_value_negate (r);
 }
 
-// Build a NAN of type TYPE.
+// Build a signless NAN of type TYPE.
 
 inline void
 frange::set_nan (tree type)
 {
-  REAL_VALUE_TYPE r;
-  gcc_assert (real_nan (&r, "", 1, TYPE_MODE (type)));
-  m_kind = VR_RANGE;
+  m_kind = VR_UNDEFINED;
+  m_type = type;
+  m_pos_nan = true;
+  m_neg_nan = true;
+  if (flag_checking)
+    verify_range ();
+}
+
+// Build a NAN of type TYPE with SIGN.
+
+inline void
+frange::set_nan (tree type, bool sign)
+{
+  m_kind = VR_UNDEFINED;
   m_type = type;
-  m_min = r;
-  m_max = r;
-  m_props.set_varying ();
-  m_props.nan_set_yes ();
+  m_neg_nan = sign;
+  m_pos_nan = !sign;
   if (flag_checking)
     verify_range ();
 }
@@ -1210,9 +1172,7 @@ frange::known_finite () const
 {
   if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE)
     return false;
-  return (!real_isnan (&m_min)
-	  && !real_isinf (&m_min)
-	  && !real_isinf (&m_max));
+  return (!maybe_nan () && !real_isinf (&m_min) && !real_isinf (&m_max));
 }
 
 // Return TRUE if range may be infinite.
@@ -1242,7 +1202,7 @@ frange::known_inf () const
 inline bool
 frange::maybe_nan () const
 {
-  return !get_nan ().no_p ();
+  return m_pos_nan || m_neg_nan;
 }
 
 // Return TRUE if range is a +NAN or -NAN.
@@ -1250,7 +1210,7 @@ frange::maybe_nan () const
 inline bool
 frange::known_nan () const
 {
-  return get_nan ().yes_p ();
+  return m_kind == VR_UNDEFINED && maybe_nan ();
 }
 
 // If the signbit for the range is known, set it in SIGNBIT and return
@@ -1259,13 +1219,31 @@ frange::known_nan () const
 inline bool
 frange::known_signbit (bool &signbit) const
 {
-  // FIXME: Signed NANs are not supported yet.
-  if (maybe_nan ())
+  if (undefined_p ())
     return false;
-  if (get_signbit ().varying_p ())
+
+  // NAN with unknown sign.
+  if (m_pos_nan && m_neg_nan)
     return false;
-  signbit = get_signbit ().yes_p ();
-  return true;
+  // No NAN.
+  if (!m_pos_nan && !m_neg_nan)
+    {
+      if (m_min.sign == m_max.sign)
+	{
+	  signbit = m_min.sign;
+	  return true;
+	}
+      return false;
+    }
+  // NAN with known sign.
+  bool nan_sign = m_neg_nan;
+  if (m_kind == VR_UNDEFINED
+      || (nan_sign == m_min.sign && nan_sign == m_max.sign))
+    {
+      signbit = nan_sign;
+      return true;
+    }
+  return false;
 }
 
 #endif // GCC_VALUE_RANGE_H
