diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index f81934af5e2d3564220ed071ba1555d69ca6c3c5..dd08dfcc222906955c8ac000f4a42ce772db0105 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1157,15 +1157,8 @@ dtoi4(PG_FUNCTION_ARGS)
 	 */
 	num = rint(num);
 
-	/*
-	 * Range check.  We must be careful here that the boundary values are
-	 * expressed exactly in the float domain.  We expect PG_INT32_MIN to be an
-	 * exact power of 2, so it will be represented exactly; but PG_INT32_MAX
-	 * isn't, and might get rounded off, so avoid using it.
-	 */
-	if (num < (float8) PG_INT32_MIN ||
-		num >= -((float8) PG_INT32_MIN) ||
-		isnan(num))
+	/* Range check */
+	if (isnan(num) || !FLOAT8_FITS_IN_INT32(num))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
@@ -1189,15 +1182,8 @@ dtoi2(PG_FUNCTION_ARGS)
 	 */
 	num = rint(num);
 
-	/*
-	 * Range check.  We must be careful here that the boundary values are
-	 * expressed exactly in the float domain.  We expect PG_INT16_MIN to be an
-	 * exact power of 2, so it will be represented exactly; but PG_INT16_MAX
-	 * isn't, and might get rounded off, so avoid using it.
-	 */
-	if (num < (float8) PG_INT16_MIN ||
-		num >= -((float8) PG_INT16_MIN) ||
-		isnan(num))
+	/* Range check */
+	if (isnan(num) || !FLOAT8_FITS_IN_INT16(num))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
@@ -1245,15 +1231,8 @@ ftoi4(PG_FUNCTION_ARGS)
 	 */
 	num = rint(num);
 
-	/*
-	 * Range check.  We must be careful here that the boundary values are
-	 * expressed exactly in the float domain.  We expect PG_INT32_MIN to be an
-	 * exact power of 2, so it will be represented exactly; but PG_INT32_MAX
-	 * isn't, and might get rounded off, so avoid using it.
-	 */
-	if (num < (float4) PG_INT32_MIN ||
-		num >= -((float4) PG_INT32_MIN) ||
-		isnan(num))
+	/* Range check */
+	if (isnan(num) || !FLOAT4_FITS_IN_INT32(num))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
@@ -1277,15 +1256,8 @@ ftoi2(PG_FUNCTION_ARGS)
 	 */
 	num = rint(num);
 
-	/*
-	 * Range check.  We must be careful here that the boundary values are
-	 * expressed exactly in the float domain.  We expect PG_INT16_MIN to be an
-	 * exact power of 2, so it will be represented exactly; but PG_INT16_MAX
-	 * isn't, and might get rounded off, so avoid using it.
-	 */
-	if (num < (float4) PG_INT16_MIN ||
-		num >= -((float4) PG_INT16_MIN) ||
-		isnan(num))
+	/* Range check */
+	if (isnan(num) || !FLOAT4_FITS_IN_INT16(num))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 40a029cc18bd2a6524add76c53db0b2ceffd6eb0..cfb094a4888092af49297a072780f61ec119b0cd 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1351,15 +1351,8 @@ dtoi8(PG_FUNCTION_ARGS)
 	 */
 	num = rint(num);
 
-	/*
-	 * Range check.  We must be careful here that the boundary values are
-	 * expressed exactly in the float domain.  We expect PG_INT64_MIN to be an
-	 * exact power of 2, so it will be represented exactly; but PG_INT64_MAX
-	 * isn't, and might get rounded off, so avoid using it.
-	 */
-	if (num < (float8) PG_INT64_MIN ||
-		num >= -((float8) PG_INT64_MIN) ||
-		isnan(num))
+	/* Range check */
+	if (isnan(num) || !FLOAT8_FITS_IN_INT64(num))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
@@ -1393,15 +1386,8 @@ ftoi8(PG_FUNCTION_ARGS)
 	 */
 	num = rint(num);
 
-	/*
-	 * Range check.  We must be careful here that the boundary values are
-	 * expressed exactly in the float domain.  We expect PG_INT64_MIN to be an
-	 * exact power of 2, so it will be represented exactly; but PG_INT64_MAX
-	 * isn't, and might get rounded off, so avoid using it.
-	 */
-	if (num < (float4) PG_INT64_MIN ||
-		num >= -((float4) PG_INT64_MIN) ||
-		isnan(num))
+	/* Range check */
+	if (isnan(num) || !FLOAT4_FITS_IN_INT64(num))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 089657371bbf4c174c44f90a1f235067daafb542..972f481e59afbce6945e4c5fed617f8949c4e796 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -3410,7 +3410,7 @@ interval_mul(PG_FUNCTION_ARGS)
 	result->day += (int32) month_remainder_days;
 #ifdef HAVE_INT64_TIMESTAMP
 	result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
-	if (result_double > PG_INT64_MAX || result_double < PG_INT64_MIN)
+	if (isnan(result_double) || !FLOAT8_FITS_IN_INT64(result_double))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("interval out of range")));
diff --git a/src/include/c.h b/src/include/c.h
index 86e4b549754649fca1a17cc6641cc25abd22ffb6..32a804a7e8186a85678fa2a5cd352934070a2be5 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -954,6 +954,30 @@ typedef NameData *Name;
 			*_start++ = 0; \
 	} while (0)
 
+/*
+ * Macros for range-checking float values before converting to integer.
+ * We must be careful here that the boundary values are expressed exactly
+ * in the float domain.  PG_INTnn_MIN is an exact power of 2, so it will
+ * be represented exactly; but PG_INTnn_MAX isn't, and might get rounded
+ * off, so avoid using that.
+ * The input must be rounded to an integer beforehand, typically with rint(),
+ * else we might draw the wrong conclusion about close-to-the-limit values.
+ * These macros will do the right thing for Inf, but not necessarily for NaN,
+ * so check isnan(num) first if that's a possibility.
+ */
+#define FLOAT4_FITS_IN_INT16(num) \
+	((num) >= (float4) PG_INT16_MIN && (num) < -((float4) PG_INT16_MIN))
+#define FLOAT4_FITS_IN_INT32(num) \
+	((num) >= (float4) PG_INT32_MIN && (num) < -((float4) PG_INT32_MIN))
+#define FLOAT4_FITS_IN_INT64(num) \
+	((num) >= (float4) PG_INT64_MIN && (num) < -((float4) PG_INT64_MIN))
+#define FLOAT8_FITS_IN_INT16(num) \
+	((num) >= (float8) PG_INT16_MIN && (num) < -((float8) PG_INT16_MIN))
+#define FLOAT8_FITS_IN_INT32(num) \
+	((num) >= (float8) PG_INT32_MIN && (num) < -((float8) PG_INT32_MIN))
+#define FLOAT8_FITS_IN_INT64(num) \
+	((num) >= (float8) PG_INT64_MIN && (num) < -((float8) PG_INT64_MIN))
+
 
 /* ----------------------------------------------------------------
  *				Section 8:	random stuff
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f34550ad56b44d37785a772e329a87c0eedb9..f772909e49ce949829fd15c80347c871f65cbc73 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -232,6 +232,9 @@ INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years');
 ERROR:  interval out of range
 LINE 1: INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'...
                                                  ^
+-- Test edge-case overflow detection in interval multiplication
+select extract(epoch from '256 microseconds'::interval * (2^55)::float8);
+ERROR:  interval out of range
 SELECT r1.*, r2.*
    FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2
    WHERE r1.f1 > r2.f1
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d1b9c008dc9b63e732b6b31b338880f8af..eb1e84f053e05ddc6d4448ec59a41911a1a8ed1e 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -73,6 +73,9 @@ INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483649 days');
 INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 years');
 INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years');
 
+-- Test edge-case overflow detection in interval multiplication
+select extract(epoch from '256 microseconds'::interval * (2^55)::float8);
+
 SELECT r1.*, r2.*
    FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2
    WHERE r1.f1 > r2.f1