diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 84d71761c1b..febc7cc2b93 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,6 +43,8 @@
 #include "optimizer/optimizer.h"
 #include "port/pg_bitutils.h"
 #include "utils/builtins.h"
+#include "utils/float.h"
+#include "utils/numeric.h"
 #include "utils/date.h"
 #include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
@@ -1872,15 +1874,31 @@ int8range_subdiff(PG_FUNCTION_ARGS)
 Datum
 numrange_subdiff(PG_FUNCTION_ARGS)
 {
-	Datum		v1 = PG_GETARG_DATUM(0);
-	Datum		v2 = PG_GETARG_DATUM(1);
-	Datum		numresult;
+	Numeric		v1 = PG_GETARG_NUMERIC(0);
+	Numeric		v2 = PG_GETARG_NUMERIC(1);
+	Numeric		numresult;
 	float8		floatresult;
+	ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-	numresult = DirectFunctionCall2(numeric_sub, v1, v2);
+	/*
+	 * The difference can exceed numeric's range, or be unrepresentable
+	 * in float8.  Neither caller is prepared to handle an error, so
+	 * saturate to infinity (or round to zero) instead.
+	 */
+	numresult = numeric_sub_safe(v1, v2, (Node *) &escontext);
+	if (SOFT_ERROR_OCCURRED(&escontext))
+		floatresult = get_float8_infinity();
+	else
+	{
+		floatresult = DatumGetFloat8(DirectFunctionCall1(
+			numeric_float8_no_overflow, NumericGetDatum(numresult)));
+		pfree(numresult);
+	}
 
-	floatresult = DatumGetFloat8(DirectFunctionCall1(numeric_float8,
-													 numresult));
+	PG_FREE_IF_COPY(v1, 0);
+	PG_FREE_IF_COPY(v2, 1);
 
 	PG_RETURN_FLOAT8(floatresult);
 }
