diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 2568fdc..e6281bc 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -568,6 +568,7 @@ int32
 numeric_maximum_size(int32 typemod)
 {
 	int precision;
+	int numeric_digits;
 
 	if (typemod <= VARHDRSZ)
 		return -1;
@@ -575,8 +576,26 @@ numeric_maximum_size(int32 typemod)
 	/* precision (ie, max # of digits) is in upper bits of typmod */
 	precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
 
-	/* Numeric stores 2 decimal digits/byte, plus header */
-	return (precision + 1) / 2 + NUMERIC_HDRSZ;
+	/*
+	 * This formula computes the maximum number of NumericDigits we could
+	 * need in order to store the specified number of decimal digits.
+	 * Because the weight is stored as a number of NumericDigits rather
+	 * than a number of decimal digits, it's possible that the first
+	 * NumericDigit will contain only a single decimal digit.  Thus, the
+	 * first two decimal digits can require two NumericDigits to store,
+	 * but it isn't until we reach DEC_DIGITS + 2 decimal digits that we
+	 * potentially need a third NumericDigit.
+	 */
+	numeric_digits = (precision + 2 * (DEC_DIGITS - 1)) / DEC_DIGITS;
+
+	/*
+	 * In most cases, the size of a numeric will be smaller than the value
+	 * computed below, because the varlena header will typically get toasted
+	 * down to a single byte before being stored on disk, and it may also
+	 * be possible to use a short numeric header.  But our job here is to
+	 * compute the worst case.
+	 */
+	return NUMERIC_HDRSZ + (numeric_digits * sizeof(NumericDigit));
 }
 
 /*
