diff --git a/gcc/poly-int.h b/gcc/poly-int.h
index 12571455081..7bff5e5ad26 100644
--- a/gcc/poly-int.h
+++ b/gcc/poly-int.h
@@ -2355,28 +2355,31 @@ can_div_trunc_p (const poly_int_pod<N, Ca> &a,
 	}
       else
 	{
-	  if (q == 0)
-	    {
-	      /* For Q == 0 we simply need: (3') |ai| <= |bi|.  */
-	      if (a.coeffs[i] != ICa (0))
-		{
-		  /* Use negative absolute to avoid overflow, i.e.
-		     -|ai| >= -|bi|.  */
-		  C neg_abs_a = (a.coeffs[i] < 0 ? a.coeffs[i] : -a.coeffs[i]);
-		  C neg_abs_b = (b.coeffs[i] < 0 ? b.coeffs[i] : -b.coeffs[i]);
-		  if (neg_abs_a < neg_abs_b)
-		    return false;
-		  rem_p = true;
-		}
-	    }
+	  /* The only unconditional arithmetic that we can do on ai,
+	     bi and Q is ai / bi and ai % bi.  (ai == minimum int and
+	     bi == -1 would be UB in the caller.)  Anything else runs
+	     the risk of overflow.  */
+	  auto qi = NCa (a.coeffs[i]) / NCb (b.coeffs[i]);
+	  auto ri = NCa (a.coeffs[i]) % NCb (b.coeffs[i]);
+	  /* (2') and (3') are satisfied when ai /[trunc] bi == q.
+	     So is the stricter condition |ai - bi * Q| < |bi|.  */
+	  if (qi == q)
+	    rem_p |= (ri != 0);
+	  /* The only other case is when:
+
+		 |bi * Q| + |bi| = |ai| (for (2'))
+	     and |ai - bi * Q|   = |bi| (for (3'))
+
+	     The first is equivalent to |bi|(|Q| + 1) == |ai|.
+	     The second requires ai == bi * (Q + 1) or ai == bi * (Q - 1).  */
+	  else if (ri != 0)
+	    return false;
+	  else if (q <= 0 && qi < q && qi + 1 == q)
+	    ;
+	  else if (q >= 0 && qi > q && qi - 1 == q)
+	    ;
 	  else
-	    {
-	      /* Otherwise just check for the case in which ai / bi == Q.  */
-	      if (NCa (a.coeffs[i]) / NCb (b.coeffs[i]) != q)
-		return false;
-	      if (NCa (a.coeffs[i]) % NCb (b.coeffs[i]) != 0)
-		rem_p = true;
-	    }
+	    return false;
 	}
     }
 
diff --git a/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h b/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h
index 0b89acd91cd..7af98595a5e 100644
--- a/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h
+++ b/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h
@@ -1899,14 +1899,19 @@ test_can_div_trunc_p_const ()
 				ph::make (4, 8, 12),
 				&const_quot));
   ASSERT_EQ (const_quot, C (2));
-  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 40),
+  ASSERT_TRUE (can_div_trunc_p (ph::make (15, 25, 40),
+				ph::make (4, 8, 10),
+				&const_quot));
+  ASSERT_EQ (const_quot, C (3));
+  const_quot = 0;
+  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 41),
 			      ph::make (4, 8, 10),
 			      &const_quot), N <= 2);
-  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 2));
+  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 0));
   ASSERT_EQ (can_div_trunc_p (ph::make (43, 79, 80),
 			      ph::make (4, 8, 10),
 			      &const_quot), N == 1);
-  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 2));
+  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 0));
   ASSERT_TRUE (can_div_trunc_p (ph::make (3, 4, 5),
 				ph::make (4, 5, 6),
 				&const_quot));
@@ -1964,16 +1969,22 @@ test_can_div_trunc_p_const ()
 				&const_quot, &rem));
   ASSERT_EQ (const_quot, C (2));
   ASSERT_KNOWN_EQ (rem, ph::make (1, 7, 6));
-  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 40),
+  ASSERT_TRUE (can_div_trunc_p (ph::make (15, 25, 40),
+				ph::make (4, 8, 10),
+				&const_quot, &rem));
+  ASSERT_EQ (const_quot, C (3));
+  ASSERT_KNOWN_EQ (rem, ph::make (3, 1, 10));
+  const_quot = 0, rem = 0;
+  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 41),
 			      ph::make (4, 8, 10),
 			      &const_quot, &rem), N <= 2);
-  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 2));
+  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 0));
   if (N <= 2)
     ASSERT_KNOWN_EQ (rem, ph::make (3, 1, 0));
   ASSERT_EQ (can_div_trunc_p (ph::make (43, 79, 80),
 			      ph::make (4, 8, 10),
 			      &const_quot, &rem), N == 1);
-  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 2));
+  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 0));
   if (N == 1)
     ASSERT_KNOWN_EQ (rem, ch::make (3));
   ASSERT_TRUE (can_div_trunc_p (ph::make (3, 4, 5),
@@ -2024,6 +2035,19 @@ test_can_div_trunc_p_const ()
 				&const_quot, &rem));
   ASSERT_EQ (const_quot, C (0));
   ASSERT_KNOWN_EQ (rem, ch::make (0));
+  ASSERT_TRUE (can_div_trunc_p (ph::make (9, 10, 20),
+				ph::make (5, 5, 20),
+				&const_quot, &rem));
+  ASSERT_EQ (const_quot, C (1));
+  ASSERT_KNOWN_EQ (rem, ph::make (4, 5, 0));
+  ASSERT_EQ (can_div_trunc_p (ph::make (9, 11, 20),
+			      ph::make (5, 5, 20),
+			      &const_quot, &rem), N == 1);
+  if (N == 1)
+    {
+      ASSERT_EQ (const_quot, C (1));
+      ASSERT_KNOWN_EQ (rem, C (4));
+    }
 }
 
 /* Test the form of can_div_trunc_p that returns a polynomail quotient,
@@ -2093,14 +2117,14 @@ test_can_div_away_from_zero_p ()
 					 ph::make (4, 8, 12),
 					 &const_quot));
   ASSERT_EQ (const_quot, C (3));
-  ASSERT_EQ (can_div_away_from_zero_p (ph::make (15, 25, 40),
-				       ph::make (4, 8, 10),
-				       &const_quot), N <= 2);
-  ASSERT_EQ (const_quot, C (N <= 2 ? 4 : 3));
+  ASSERT_TRUE (can_div_away_from_zero_p (ph::make (15, 25, 40),
+					 ph::make (4, 8, 10),
+					 &const_quot));
+  ASSERT_EQ (const_quot, C (4));
   ASSERT_EQ (can_div_away_from_zero_p (ph::make (43, 79, 80),
 				       ph::make (4, 8, 10),
 				       &const_quot), N == 1);
-  ASSERT_EQ (const_quot, C (N == 1 ? 11 : N == 2 ? 4 : 3));
+  ASSERT_EQ (const_quot, C (N == 1 ? 11 : 4));
   ASSERT_TRUE (can_div_away_from_zero_p (ph::make (3, 4, 5),
 					 ph::make (4, 5, 6),
 					 &const_quot));
@@ -3232,6 +3256,45 @@ test_signed_can_div_trunc_p_const ()
 				&const_quot, &rem));
   ASSERT_EQ (const_quot, -2);
   ASSERT_KNOWN_EQ (rem, ph::make (2, 1, 3));
+  ASSERT_TRUE (can_div_trunc_p (ph::make (-9, -10, -20),
+				ph::make (-5, -5, -20),
+				&const_quot, &rem));
+  ASSERT_EQ (const_quot, C (1));
+  ASSERT_KNOWN_EQ (rem, ph::make (-4, -5, 0));
+  ASSERT_EQ (can_div_trunc_p (ph::make (-9, -11, -20),
+			      ph::make (-5, -5, -20),
+			      &const_quot, &rem), N == 1);
+  if (N == 1)
+    {
+      ASSERT_EQ (const_quot, C (1));
+      ASSERT_KNOWN_EQ (rem, C (-4));
+    }
+  ASSERT_TRUE (can_div_trunc_p (ph::make (9, 10, 20),
+				ph::make (-5, -5, -20),
+				&const_quot, &rem));
+  ASSERT_EQ (const_quot, C (-1));
+  ASSERT_KNOWN_EQ (rem, ph::make (4, 5, 0));
+  ASSERT_EQ (can_div_trunc_p (ph::make (9, 11, 20),
+			      ph::make (-5, -5, -20),
+			      &const_quot, &rem), N == 1);
+  if (N == 1)
+    {
+      ASSERT_EQ (const_quot, C (-1));
+      ASSERT_KNOWN_EQ (rem, C (4));
+    }
+  ASSERT_TRUE (can_div_trunc_p (ph::make (-9, -10, -20),
+				ph::make (5, 5, 20),
+				&const_quot, &rem));
+  ASSERT_EQ (const_quot, C (-1));
+  ASSERT_KNOWN_EQ (rem, ph::make (-4, -5, 0));
+  ASSERT_EQ (can_div_trunc_p (ph::make (-9, -11, -20),
+			      ph::make (5, 5, 20),
+			      &const_quot, &rem), N == 1);
+  if (N == 1)
+    {
+      ASSERT_EQ (const_quot, C (-1));
+      ASSERT_KNOWN_EQ (rem, C (-4));
+    }
 }
 
 /* Test the form of can_div_trunc_p that returns a poly_int, for signed C.  */
