diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index ff42b95de4f2153641013d90262a484527153360..63b7ad1a008d21bab6eedb0cbe3aa9716a4302ae 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -2199,6 +2199,33 @@ zero_to_inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known)
     }
 }
 
+/* Extend the LHS range by 1ulp in each direction.  For op1_range
+   or op2_range of binary operations just computing the inverse
+   operation on ranges isn't sufficient.  Consider e.g.
+   [1., 1.] = op1 + [1., 1.].  op1's range is not [0., 0.], but
+   [-0x1.0p-54, 0x1.0p-53] (when not -frounding-math), any value for
+   which adding 1. to it results in 1. after rounding to nearest.
+   So, for op1_range/op2_range extend the lhs range by 1ulp in each
+   direction.  See PR109008 for more details.  */
+
+static frange
+float_widen_lhs_range (tree type, const frange &lhs)
+{
+  frange ret = lhs;
+  if (lhs.known_isnan ())
+    return ret;
+  REAL_VALUE_TYPE lb = lhs.lower_bound ();
+  REAL_VALUE_TYPE ub = lhs.upper_bound ();
+  if (real_isfinite (&lb))
+    frange_nextafter (TYPE_MODE (type), lb, dconstninf);
+  if (real_isfinite (&ub))
+    frange_nextafter (TYPE_MODE (type), ub, dconstinf);
+  ret.set (type, lb, ub);
+  ret.clear_nan ();
+  ret.union_ (lhs);
+  return ret;
+}
+
 class foperator_plus : public range_operator_float
 {
   using range_operator_float::op1_range;
@@ -2214,8 +2241,9 @@ public:
     range_op_handler minus (MINUS_EXPR, type);
     if (!minus)
       return false;
-    return float_binary_op_range_finish (minus.fold_range (r, type, lhs, op2),
-					 r, type, lhs);
+    frange wlhs = float_widen_lhs_range (type, lhs);
+    return float_binary_op_range_finish (minus.fold_range (r, type, wlhs, op2),
+					 r, type, wlhs);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2260,9 +2288,10 @@ public:
   {
     if (lhs.undefined_p ())
       return false;
-    return float_binary_op_range_finish (fop_plus.fold_range (r, type, lhs,
+    frange wlhs = float_widen_lhs_range (type, lhs);
+    return float_binary_op_range_finish (fop_plus.fold_range (r, type, wlhs,
 							      op2),
-					 r, type, lhs);
+					 r, type, wlhs);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2271,8 +2300,9 @@ public:
   {
     if (lhs.undefined_p ())
       return false;
-    return float_binary_op_range_finish (fold_range (r, type, op1, lhs),
-					 r, type, lhs);
+    frange wlhs = float_widen_lhs_range (type, lhs);
+    return float_binary_op_range_finish (fold_range (r, type, op1, wlhs),
+					 r, type, wlhs);
   }
 private:
   void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
@@ -2338,13 +2368,14 @@ public:
     range_op_handler rdiv (RDIV_EXPR, type);
     if (!rdiv)
       return false;
-    bool ret = rdiv.fold_range (r, type, lhs, op2);
+    frange wlhs = float_widen_lhs_range (type, lhs);
+    bool ret = rdiv.fold_range (r, type, wlhs, op2);
     if (ret == false)
       return false;
-    if (lhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
-      return float_binary_op_range_finish (ret, r, type, lhs);
-    const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound ();
-    const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound ();
+    if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
+      return float_binary_op_range_finish (ret, r, type, wlhs);
+    const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound ();
+    const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound ();
     const REAL_VALUE_TYPE &op2_lb = op2.lower_bound ();
     const REAL_VALUE_TYPE &op2_ub = op2.upper_bound ();
     if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op2_lb, op2_ub))
@@ -2363,7 +2394,7 @@ public:
     // or if lhs must be zero and op2 doesn't include zero, it would be
     // UNDEFINED, while rdiv.fold_range computes a zero or singleton INF
     // range.  Those are supersets of UNDEFINED, so let's keep that way.
-    return float_binary_op_range_finish (ret, r, type, lhs);
+    return float_binary_op_range_finish (ret, r, type, wlhs);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2490,13 +2521,14 @@ public:
   {
     if (lhs.undefined_p ())
       return false;
-    bool ret = fop_mult.fold_range (r, type, lhs, op2);
+    frange wlhs = float_widen_lhs_range (type, lhs);
+    bool ret = fop_mult.fold_range (r, type, wlhs, op2);
     if (!ret)
       return ret;
-    if (lhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
-      return float_binary_op_range_finish (ret, r, type, lhs);
-    const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound ();
-    const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound ();
+    if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
+      return float_binary_op_range_finish (ret, r, type, wlhs);
+    const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound ();
+    const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound ();
     const REAL_VALUE_TYPE &op2_lb = op2.lower_bound ();
     const REAL_VALUE_TYPE &op2_ub = op2.upper_bound ();
     if ((contains_zero_p (lhs_lb, lhs_ub)
@@ -2512,7 +2544,7 @@ public:
 	zero_to_inf_range (lb, ub, signbit_known);
 	r.set (type, lb, ub);
       }
-    return float_binary_op_range_finish (ret, r, type, lhs);
+    return float_binary_op_range_finish (ret, r, type, wlhs);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2521,13 +2553,14 @@ public:
   {
     if (lhs.undefined_p ())
       return false;
-    bool ret = fold_range (r, type, op1, lhs);
+    frange wlhs = float_widen_lhs_range (type, lhs);
+    bool ret = fold_range (r, type, op1, wlhs);
     if (!ret)
       return ret;
-    if (lhs.known_isnan () || op1.known_isnan () || op1.undefined_p ())
-      return float_binary_op_range_finish (ret, r, type, lhs, true);
-    const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound ();
-    const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound ();
+    if (wlhs.known_isnan () || op1.known_isnan () || op1.undefined_p ())
+      return float_binary_op_range_finish (ret, r, type, wlhs, true);
+    const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound ();
+    const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound ();
     const REAL_VALUE_TYPE &op1_lb = op1.lower_bound ();
     const REAL_VALUE_TYPE &op1_ub = op1.upper_bound ();
     if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op1_lb, op1_ub))
@@ -2542,7 +2575,7 @@ public:
 	zero_to_inf_range (lb, ub, signbit_known);
 	r.set (type, lb, ub);
       }
-    return float_binary_op_range_finish (ret, r, type, lhs, true);
+    return float_binary_op_range_finish (ret, r, type, wlhs, true);
   }
 private:
   void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
diff --git a/gcc/testsuite/gcc.c-torture/execute/ieee/pr109008.c b/gcc/testsuite/gcc.c-torture/execute/ieee/pr109008.c
new file mode 100644
index 0000000000000000000000000000000000000000..610aed4cac01f71b7f2a74eaa6470c0ccb5c1498
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/ieee/pr109008.c
@@ -0,0 +1,18 @@
+/* PR tree-optimization/109008 */
+
+__attribute__((noipa)) double
+foo (double eps)
+{
+  double d = 1. + eps;
+  if (d == 1.)
+    return eps;
+  return 0.0;
+}
+
+int
+main ()
+{
+  if (foo (__DBL_EPSILON__ / 8.0) == 0.0)
+    __builtin_abort ();
+  return 0;
+}