https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126641
Aldy Hernandez <aldyh at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |jakub at gcc dot gnu.org
--- Comment #9 from Aldy Hernandez <aldyh at gcc dot gnu.org> ---
I am not an FP expert, not do I play one on TV.
I debugged this with heavy help from Claude. Yeah, don’t shoot me.
Here is a testcase that I believe shows the problem.
FWIW, we should probably keep that assert.
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index baf8a6dc098..ca443201d7b 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -3277,6 +3277,19 @@ range_op_float_tests ()
REAL_VALUE_TYPE five;
real_from_string (&five, "5.0");
ASSERT_FALSE (r.contains_p (five));
+
+ // float_widen_lhs_range widens each sub-range bound by
+ // half an ulp, so a bound at the smallest denormal lands just below it.
When
+ // frange_fusible_p then calls real_nextafter on that sub-denormal bound,
+ // real_nextafter's np2 reaches SIGNIFICAND_BITS and set_significand_bit
stores
+ // one past sig[]
+ r0 = frange_float ("-1.0", "-0x1p-149");
+ r1 = frange_float ("0x1p-149", "1.0");
+ r0.union_ (r1);
+ r0.clear_nan ();
+ ASSERT_EQ (r0.num_pairs (), 2);
+ r = float_widen_lhs_range (float_type_node, r0);
+ ASSERT_EQ (r.num_pairs (), 2);
}
} // namespace selftest
diff --git a/gcc/real.cc b/gcc/real.cc
index 29cb6a1edd2..6a76d26ad65 100644
--- a/gcc/real.cc
+++ b/gcc/real.cc
@@ -399,6 +399,7 @@ cmp_significand_0 (const REAL_VALUE_TYPE *a)
static inline void
set_significand_bit (REAL_VALUE_TYPE *r, unsigned int n)
{
+ gcc_assert (n < SIGNIFICAND_BITS);
r->sig[n / HOST_BITS_PER_LONG]
|= (unsigned long)1 << (n % HOST_BITS_PER_LONG);
}