From: Aldy Hernandez Date: Sun, 19 Jul 2026 09:38:49 +0000 (+0000) Subject: [frange] Store a single sub-range in an array slot. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bbf71b94fc1af926cb9dfe3934e716ced755dc8b;p=thirdparty%2Fgcc.git [frange] Store a single sub-range in an array slot. To prepare for holding more than one sub-range, move the endpoints m_min/m_max into a one-element array. While here, pick the low-hanging multi-range fruit so the follow-up patches stay small: consumers that walk every sub-range now loop over m_num_ranges, and the inherently single-range ones (normalize_kind, singleton_p, etc) gain an m_num_ranges == 1 guard. The hard cases (union_/intersect, etc) are left with FIXMEs for later. No change to functionality as MAX_PAIRS is 1. I'll bump this to 2 in a subequent patch, and implement union/intersect and all the other little things. Tested on ppc64le Linux. No change to assembly over the LAPACK preprocessed-Fortran corpus; LAPACK and GCC tests pass. gcc/ChangeLog: * value-range.h (struct frange_pair): New. (class frange): Replace m_min and m_max with m_pairs[MAX_PAIRS] and m_num_ranges. Add MAX_PAIRS, num_pairs, and the indexed lower_bound and upper_bound. Have canonicalize_zeros take an frange_pair. (frange::lower_bound, frange::upper_bound): Adjust. (frange::set_varying, frange::set_undefined, frange::set_nan) (frange::known_isfinite, frange::known_isnormal) (frange::known_isdenormal_or_zero, frange::maybe_isinf) (frange::known_isinf, frange::signbit_p): Adjust. * value-range.cc (frange::flush_denormals_to_zero): Adjust. (frange::canonicalize_zeros): Take an frange_pair. (frange::set, frange::normalize_kind, frange::union_nans) (frange::union_, frange::intersect, frange::operator=) (frange::operator==, frange::contains_p) (frange::internal_singleton_p, frange::singleton_p) (frange::verify_range, frange::zero_p): Adjust. * value-range-storage.h (class frange_storage): Replace m_min and m_max with m_pairs and m_num_ranges. * value-range-storage.cc (frange_storage::set_frange): Adjust. (frange_storage::get_frange): Adjust. --- diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc index 837c40bb72e..6f8b53814a8 100644 --- a/gcc/value-range-storage.cc +++ b/gcc/value-range-storage.cc @@ -524,8 +524,9 @@ frange_storage::set_frange (const frange &r) gcc_checking_assert (fits_p (r)); m_kind = r.m_kind; - m_min = r.m_min; - m_max = r.m_max; + m_num_ranges = r.m_num_ranges; + for (unsigned i = 0; i < r.m_num_ranges; ++i) + m_pairs[i] = r.m_pairs[i]; m_pos_nan = r.m_pos_nan; m_neg_nan = r.m_neg_nan; } @@ -555,13 +556,11 @@ frange_storage::get_frange (frange &r, tree type) const return; } - // We use the constructor to create the new range instead of writing - // out the bits into the frange directly, because the global range - // being read may be being inlined into a function with different - // restrictions as when it was originally written. We want to make - // sure the resulting range is canonicalized correctly for the new - // consumer. - r = frange (type, m_min, m_max, m_kind); + // FIXME: Rewrite for sub-ranges. This only reconstructs the first pair. + // Eventually do it piecewise like irange_storage::get_irange: start + // undefined and union each sub-range built through the constructor (so + // every piece is re-canonicalized). + r = frange (type, m_pairs[0].min, m_pairs[0].max, m_kind); // The constructor will set the NAN bits for HONOR_NANS, but we must // make sure to set the NAN sign if known. diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h index e289765cfe0..a7369d1eb0f 100644 --- a/gcc/value-range-storage.h +++ b/gcc/value-range-storage.h @@ -167,8 +167,8 @@ class GTY((tag ("VR_FRANGE"))) frange_storage : public vrange_storage DISABLE_COPY_AND_ASSIGN (frange_storage); enum value_range_kind m_kind; - REAL_VALUE_TYPE m_min; - REAL_VALUE_TYPE m_max; + frange_pair m_pairs[frange::MAX_PAIRS]; + unsigned char m_num_ranges; bool m_pos_nan; bool m_neg_nan; }; diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 4e1be3b0ed6..09381e62602 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -1023,6 +1023,7 @@ frange::flush_denormals_to_zero () machine_mode mode = TYPE_MODE (type ()); + // FIXME: Rewrite for sub-ranges. // Flush a denormal endpoint to a zero of the same sign: a +denormal lower // bound to +0.0, and a -denormal upper bound to -0.0. Then call // canonicalize_zeros to rewrite the sign to whatever the flags make @@ -1033,15 +1034,15 @@ frange::flush_denormals_to_zero () // // keeping contains_p (-0.0) true; under HONOR_SIGNED_ZEROS the sign stands and // it stays [ +0.0, 5.0 ]. - if (real_isdenormal (&m_max, mode) && real_isneg (&m_max)) - m_max = dconstm0; - if (real_isdenormal (&m_min, mode) && !real_isneg (&m_min)) - m_min = dconst0; - canonicalize_zeros (m_min, m_max); + if (real_isdenormal (&m_pairs[0].max, mode) && real_isneg (&m_pairs[0].max)) + m_pairs[0].max = dconstm0; + if (real_isdenormal (&m_pairs[0].min, mode) && !real_isneg (&m_pairs[0].min)) + m_pairs[0].min = dconst0; + canonicalize_zeros (m_pairs[0]); } -// Canonicalize the signed zeros of the endpoints MIN and MAX according with what -// the target and flags want: +// Canonicalize the signed zeros of a sub-range according with what the target +// and flags want: // // !MODE_HAS_SIGNED_ZEROS: the mode has no signed zero, so any zero is +0.0. // @@ -1051,21 +1052,21 @@ frange::flush_denormals_to_zero () // Otherwise the sign is a real distinction, and we keep it. void -frange::canonicalize_zeros (REAL_VALUE_TYPE &min, REAL_VALUE_TYPE &max) +frange::canonicalize_zeros (frange_pair &p) { if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type))) { - if (real_iszero (&min, 1)) - min.sign = 0; - if (real_iszero (&max, 1)) - max.sign = 0; + if (real_iszero (&p.min, 1)) + p.min.sign = 0; + if (real_iszero (&p.max, 1)) + p.max.sign = 0; } else if (!HONOR_SIGNED_ZEROS (m_type)) { - if (real_iszero (&max, 1)) - max.sign = 0; - if (real_iszero (&min, 0)) - min.sign = 1; + if (real_iszero (&p.max, 1)) + p.max.sign = 0; + if (real_iszero (&p.min, 0)) + p.min.sign = 1; } } @@ -1095,8 +1096,9 @@ frange::set (tree type, m_kind = kind; m_type = type; - m_min = min; - m_max = max; + m_num_ranges = 1; + m_pairs[0].min = min; + m_pairs[0].max = max; if (HONOR_NANS (m_type)) { m_pos_nan = nan.pos_p (); @@ -1108,7 +1110,7 @@ frange::set (tree type, m_neg_nan = false; } - canonicalize_zeros (m_min, m_max); + canonicalize_zeros (m_pairs[0]); // For -ffinite-math-only we can drop ranges outside the // representable numbers to min/max for the type. @@ -1116,14 +1118,14 @@ frange::set (tree type, { REAL_VALUE_TYPE min_repr = frange_val_min (m_type); REAL_VALUE_TYPE max_repr = frange_val_max (m_type); - if (real_less (&m_min, &min_repr)) - m_min = min_repr; - else if (real_less (&max_repr, &m_min)) - m_min = max_repr; - if (real_less (&max_repr, &m_max)) - m_max = max_repr; - else if (real_less (&m_max, &min_repr)) - m_max = min_repr; + if (real_less (&m_pairs[0].min, &min_repr)) + m_pairs[0].min = min_repr; + else if (real_less (&max_repr, &m_pairs[0].min)) + m_pairs[0].min = max_repr; + if (real_less (&max_repr, &m_pairs[0].max)) + m_pairs[0].max = max_repr; + else if (real_less (&m_pairs[0].max, &min_repr)) + m_pairs[0].max = min_repr; } // Check for swapped ranges. @@ -1162,8 +1164,9 @@ bool frange::normalize_kind () { if (m_kind == VR_RANGE - && frange_val_is_min (m_min, m_type) - && frange_val_is_max (m_max, m_type)) + && m_num_ranges == 1 + && frange_val_is_min (m_pairs[0].min, m_type) + && frange_val_is_max (m_pairs[0].max, m_type)) { if (!HONOR_NANS (m_type) || (m_pos_nan && m_neg_nan)) { @@ -1176,8 +1179,9 @@ frange::normalize_kind () if (HONOR_NANS (m_type) && (!m_pos_nan || !m_neg_nan)) { m_kind = VR_RANGE; - m_min = frange_val_min (m_type); - m_max = frange_val_max (m_type); + m_num_ranges = 1; + m_pairs[0].min = frange_val_min (m_type); + m_pairs[0].max = frange_val_max (m_type); if (flag_checking) verify_range (); return true; @@ -1199,8 +1203,9 @@ frange::union_nans (const frange &r) if (known_isnan () && m_kind != r.m_kind) { m_kind = r.m_kind; - m_min = r.m_min; - m_max = r.m_max; + m_num_ranges = r.m_num_ranges; + for (unsigned i = 0; i < r.m_num_ranges; ++i) + m_pairs[i] = r.m_pairs[i]; changed = true; } if (m_pos_nan != r.m_pos_nan || m_neg_nan != r.m_neg_nan) @@ -1241,15 +1246,16 @@ frange::union_ (const vrange &v) changed = true; } - // Combine endpoints. - if (frange_cmp (r.m_min, m_min) < 0) + // FIXME: Rewrite for sub-ranges. + // Combine endpoints. This needs to be rewritten for sub-ranges. + if (frange_cmp (r.m_pairs[0].min, m_pairs[0].min) < 0) { - m_min = r.m_min; + m_pairs[0].min = r.m_pairs[0].min; changed = true; } - if (frange_cmp (m_max, r.m_max) < 0) + if (frange_cmp (m_pairs[0].max, r.m_pairs[0].max) < 0) { - m_max = r.m_max; + m_pairs[0].max = r.m_pairs[0].max; changed = true; } @@ -1304,21 +1310,23 @@ frange::intersect (const vrange &v) changed = true; } + // FIXME: Rewrite for sub-ranges. // Combine endpoints. - if (frange_cmp (m_min, r.m_min) < 0) + if (frange_cmp (m_pairs[0].min, r.m_pairs[0].min) < 0) { - m_min = r.m_min; + m_pairs[0].min = r.m_pairs[0].min; changed = true; } - if (frange_cmp (r.m_max, m_max) < 0) + if (frange_cmp (r.m_pairs[0].max, m_pairs[0].max) < 0) { - m_max = r.m_max; + m_pairs[0].max = r.m_pairs[0].max; changed = true; } + // FIXME: Rewrite for sub-ranges. // If the endpoints are swapped, the resulting range is empty. This also // catches [+0.0, -0.0], which is also empty. - if (frange_cmp (m_max, m_min) < 0) + if (frange_cmp (m_pairs[0].max, m_pairs[0].min) < 0) { if (maybe_isnan ()) m_kind = VR_NAN; @@ -1338,8 +1346,9 @@ 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_num_ranges = src.m_num_ranges; + for (unsigned i = 0; i < src.m_num_ranges; ++i) + m_pairs[i] = src.m_pairs[i]; m_pos_nan = src.m_pos_nan; m_neg_nan = src.m_neg_nan; @@ -1369,9 +1378,14 @@ frange::operator== (const frange &src) const return false; } - return (real_identical (&m_min, &src.m_min) - && real_identical (&m_max, &src.m_max) - && m_pos_nan == src.m_pos_nan + if (m_num_ranges != src.m_num_ranges) + return false; + for (unsigned i = 0; i < m_num_ranges; ++i) + if (!real_identical (&m_pairs[i].min, &src.m_pairs[i].min) + || !real_identical (&m_pairs[i].max, &src.m_pairs[i].max)) + return false; + + return (m_pos_nan == src.m_pos_nan && m_neg_nan == src.m_neg_nan && types_compatible_p (m_type, src.m_type)); } @@ -1404,7 +1418,12 @@ frange::contains_p (const REAL_VALUE_TYPE &r) const if (known_isnan ()) return false; - return frange_cmp (r, m_min) >= 0 && frange_cmp (r, m_max) <= 0; + for (unsigned i = 0; i < m_num_ranges; ++i) + if (frange_cmp (r, m_pairs[i].min) >= 0 + && frange_cmp (r, m_pairs[i].max) <= 0) + return true; + + return false; } // If range is a singleton, place it in RESULT and return TRUE. If @@ -1415,7 +1434,9 @@ frange::contains_p (const REAL_VALUE_TYPE &r) const bool frange::internal_singleton_p (REAL_VALUE_TYPE *result) const { - if (m_kind == VR_RANGE && real_identical (&m_min, &m_max)) + if (m_kind == VR_RANGE + && m_num_ranges == 1 + && real_identical (&m_pairs[0].min, &m_pairs[0].max)) { // Return false for any singleton that may be a NAN. if (HONOR_NANS (m_type) && maybe_isnan ()) @@ -1428,16 +1449,16 @@ frange::internal_singleton_p (REAL_VALUE_TYPE *result) const // or -0.0. Since this means there is more than one way to // represent a value, return false to avoid propagating it. // See libgcc/config/rs6000/ibm-ldouble-format for details. - if (real_isinf (&m_min)) + if (real_isinf (&m_pairs[0].min)) return false; REAL_VALUE_TYPE r; - real_convert (&r, DFmode, &m_min); - if (real_identical (&r, &m_min)) + real_convert (&r, DFmode, &m_pairs[0].min); + if (real_identical (&r, &m_pairs[0].min)) return false; } if (result) - *result = m_min; + *result = m_pairs[0].min; return true; } return false; @@ -1449,7 +1470,7 @@ frange::singleton_p (tree *result) const if (internal_singleton_p ()) { if (result) - *result = build_real (m_type, m_min); + *result = build_real (m_type, m_pairs[0].min); return true; } return false; @@ -1479,8 +1500,9 @@ frange::verify_range () const return; case VR_VARYING: gcc_checking_assert (m_type); - gcc_checking_assert (frange_val_is_min (m_min, m_type)); - gcc_checking_assert (frange_val_is_max (m_max, m_type)); + gcc_checking_assert (m_num_ranges == 1); + gcc_checking_assert (frange_val_is_min (m_pairs[0].min, m_type)); + gcc_checking_assert (frange_val_is_max (m_pairs[0].max, m_type)); if (HONOR_NANS (m_type)) gcc_checking_assert (m_pos_nan && m_neg_nan); else @@ -1497,24 +1519,32 @@ frange::verify_range () const gcc_unreachable (); } - // NANs cannot appear in the endpoints of a range. - gcc_checking_assert (!real_isnan (&m_min) && !real_isnan (&m_max)); + for (unsigned i = 0; i < m_num_ranges; ++i) + { + // NANs cannot appear in the endpoints of a range. + gcc_checking_assert (!real_isnan (&m_pairs[i].min) + && !real_isnan (&m_pairs[i].max)); - // Make sure we don't have swapped ranges. This also catches [ +0.0, -0.0]. - gcc_checking_assert (frange_cmp (m_min, m_max) <= 0); + // Make sure we don't have swapped ranges. + // This also catches [ +0.0, -0.0]. + gcc_checking_assert (frange_cmp (m_pairs[i].min, m_pairs[i].max) <= 0); - // A zero endpoint must carry its canonical sign. Every producer runs - // canonicalize_zeros, so a zero bound can only descend from a canonical one. - if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type))) - gcc_checking_assert (!real_iszero (&m_min, 1) && !real_iszero (&m_max, 1)); - else if (!HONOR_SIGNED_ZEROS (m_type)) - gcc_checking_assert (!real_iszero (&m_min, 0) && !real_iszero (&m_max, 1)); + // A zero endpoint must carry its canonical sign. Every producer runs + // canonicalize_zeros, so a zero bound can only descend from a canonical + // one. + if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type))) + gcc_checking_assert (!real_iszero (&m_pairs[i].min, 1) + && !real_iszero (&m_pairs[i].max, 1)); + else if (!HONOR_SIGNED_ZEROS (m_type)) + gcc_checking_assert (!real_iszero (&m_pairs[i].min, 0) + && !real_iszero (&m_pairs[i].max, 1)); + } // If all the properties are clear, we better not span the entire // domain, because that would make us varying. - if (m_pos_nan && m_neg_nan) - gcc_checking_assert (!frange_val_is_min (m_min, m_type) - || !frange_val_is_max (m_max, m_type)); + if (m_num_ranges == 1 && m_pos_nan && m_neg_nan) + gcc_checking_assert (!frange_val_is_min (m_pairs[0].min, m_type) + || !frange_val_is_max (m_pairs[0].max, m_type)); } // We can't do much with nonzeros yet. @@ -1552,8 +1582,9 @@ bool frange::zero_p () const { return (m_kind == VR_RANGE - && real_iszero (&m_min) - && real_iszero (&m_max)); + && m_num_ranges == 1 + && real_iszero (&m_pairs[0].min) + && real_iszero (&m_pairs[0].max)); } // Set the range to non-negative numbers, that is [+0.0, +INF]. diff --git a/gcc/value-range.h b/gcc/value-range.h index 5bea3cbc9d4..2ea0006ba9f 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -584,10 +584,18 @@ nan_state::neg_p () const return m_neg_nan; } +// A sub-range in an frange. + +struct frange_pair +{ + REAL_VALUE_TYPE min; + REAL_VALUE_TYPE max; +}; + // A subset of possible values for a floating point type. // -// The representation is a type with a couple of endpoints, unioned -// with a subset of { -NaN, +NaN }. +// The representation is a single interval, unioned with a subset of +// { -NaN, +NaN }. class frange final : public vrange { @@ -657,6 +665,11 @@ public: bool known_isnormal () const; bool known_isdenormal_or_zero () const; virtual void verify_range () const override; + + static const unsigned int MAX_PAIRS = 1; + unsigned num_pairs () const { return m_num_ranges; } + const REAL_VALUE_TYPE &lower_bound (unsigned pair) const; + const REAL_VALUE_TYPE &upper_bound (unsigned pair) const; protected: virtual bool contains_p (tree cst) const override; virtual void set (tree, tree, value_range_kind = VR_RANGE) override; @@ -666,11 +679,11 @@ private: bool normalize_kind (); bool union_nans (const frange &); bool intersect_nans (const frange &); - void canonicalize_zeros (REAL_VALUE_TYPE &, REAL_VALUE_TYPE &); + void canonicalize_zeros (frange_pair &); tree m_type; - REAL_VALUE_TYPE m_min; - REAL_VALUE_TYPE m_max; + frange_pair m_pairs[MAX_PAIRS]; + unsigned char m_num_ranges; bool m_pos_nan; bool m_neg_nan; }; @@ -679,14 +692,30 @@ inline const REAL_VALUE_TYPE & frange::lower_bound () const { gcc_checking_assert (!undefined_p () && !known_isnan ()); - return m_min; + return m_pairs[0].min; } inline const REAL_VALUE_TYPE & frange::upper_bound () const { gcc_checking_assert (!undefined_p () && !known_isnan ()); - return m_max; + return m_pairs[m_num_ranges - 1].max; +} + +inline const REAL_VALUE_TYPE & +frange::lower_bound (unsigned pair) const +{ + gcc_checking_assert (!undefined_p () && !known_isnan ()); + gcc_checking_assert (pair < m_num_ranges); + return m_pairs[pair].min; +} + +inline const REAL_VALUE_TYPE & +frange::upper_bound (unsigned pair) const +{ + gcc_checking_assert (!undefined_p () && !known_isnan ()); + gcc_checking_assert (pair < m_num_ranges); + return m_pairs[pair].max; } // Return the NAN state. @@ -1634,8 +1663,9 @@ frange::set_varying (tree type) { m_kind = VR_VARYING; m_type = type; - m_min = frange_val_min (type); - m_max = frange_val_max (type); + m_num_ranges = 1; + m_pairs[0].min = frange_val_min (type); + m_pairs[0].max = frange_val_max (type); if (HONOR_NANS (m_type)) { m_pos_nan = true; @@ -1653,9 +1683,10 @@ frange::set_undefined () { m_kind = VR_UNDEFINED; m_type = NULL; + m_num_ranges = 1; m_pos_nan = false; m_neg_nan = false; - // m_min and m_min are uninitialized as they are REAL_VALUE_TYPE ??. + // Leave the rest undefined; as it speeds up initializing undefined ranges. if (flag_checking) verify_range (); } @@ -1789,6 +1820,7 @@ frange::set_nan (tree type, const nan_state &nan) { m_kind = VR_NAN; m_type = type; + m_num_ranges = 1; m_neg_nan = nan.neg_p (); m_pos_nan = nan.pos_p (); if (flag_checking) @@ -1823,7 +1855,9 @@ frange::known_isfinite () const { if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE) return false; - return (!maybe_isnan () && !real_isinf (&m_min) && !real_isinf (&m_max)); + return (!maybe_isnan () + && !real_isinf (&lower_bound ()) + && !real_isinf (&upper_bound ())); } // Return TRUE if range is known to be normal. @@ -1835,9 +1869,11 @@ frange::known_isnormal () const return false; machine_mode mode = TYPE_MODE (type ()); - return (!real_isdenormal (&m_min, mode) && !real_isdenormal (&m_max, mode) - && !real_iszero (&m_min) && !real_iszero (&m_max) - && (!real_isneg (&m_min) || real_isneg (&m_max))); + const REAL_VALUE_TYPE &min = lower_bound (); + const REAL_VALUE_TYPE &max = upper_bound (); + return (!real_isdenormal (&min, mode) && !real_isdenormal (&max, mode) + && !real_iszero (&min) && !real_iszero (&max) + && (!real_isneg (&min) || real_isneg (&max))); } // Return TRUE if range is known to be denormal. @@ -1849,8 +1885,10 @@ frange::known_isdenormal_or_zero () const return false; machine_mode mode = TYPE_MODE (type ()); - return ((real_isdenormal (&m_min, mode) || real_iszero (&m_min)) - && (real_isdenormal (&m_max, mode) || real_iszero (&m_max))); + const REAL_VALUE_TYPE &min = lower_bound (); + const REAL_VALUE_TYPE &max = upper_bound (); + return ((real_isdenormal (&min, mode) || real_iszero (&min)) + && (real_isdenormal (&max, mode) || real_iszero (&max))); } // Return TRUE if range may be infinite. @@ -1862,7 +1900,7 @@ frange::maybe_isinf () const return false; if (varying_p ()) return true; - return real_isinf (&m_min) || real_isinf (&m_max); + return real_isinf (&lower_bound ()) || real_isinf (&upper_bound ()); } // Return TRUE if range is known to be the [-INF,-INF] or [+INF,+INF]. @@ -1871,9 +1909,10 @@ inline bool frange::known_isinf () const { return (m_kind == VR_RANGE + && m_num_ranges == 1 && !maybe_isnan () - && real_identical (&m_min, &m_max) - && real_isinf (&m_min)); + && real_identical (&m_pairs[0].min, &m_pairs[0].max) + && real_isinf (&m_pairs[0].min)); } // Return TRUE if range is possibly a NAN. @@ -1921,9 +1960,9 @@ frange::signbit_p (bool &signbit) const // No NAN. if (!m_pos_nan && !m_neg_nan) { - if (m_min.sign == m_max.sign) + if (lower_bound ().sign == upper_bound ().sign) { - signbit = m_min.sign; + signbit = lower_bound ().sign; return true; } return false; @@ -1931,7 +1970,8 @@ frange::signbit_p (bool &signbit) const // NAN with known sign. bool nan_sign = m_neg_nan; if (known_isnan () - || (nan_sign == m_min.sign && nan_sign == m_max.sign)) + || (nan_sign == lower_bound ().sign + && nan_sign == upper_bound ().sign)) { signbit = nan_sign; return true;