Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 277989)
+++ gcc/builtins.c	(working copy)
@@ -9039,7 +9039,7 @@ fold_builtin_interclass_mathfn (location_t loc, tr
 	    mode = DFmode;
 	    arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
 	  }
-	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
 	real_from_string (&r, buf);
 	result = build_call_expr (isgr_fn, 2,
 				  fold_build1_loc (loc, ABS_EXPR, type, arg),
@@ -9063,7 +9063,7 @@ fold_builtin_interclass_mathfn (location_t loc, tr
 	    mode = DFmode;
 	    arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
 	  }
-	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
 	real_from_string (&r, buf);
 	result = build_call_expr (isle_fn, 2,
 				  fold_build1_loc (loc, ABS_EXPR, type, arg),
@@ -9102,7 +9102,7 @@ fold_builtin_interclass_mathfn (location_t loc, tr
 	  }
 	arg = fold_build1_loc (loc, ABS_EXPR, type, arg);
 
-	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
 	real_from_string (&rmax, buf);
 	sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (orig_mode)->emin - 1);
 	real_from_string (&rmin, buf);
Index: gcc/c-family/c-cppbuiltin.c
===================================================================
--- gcc/c-family/c-cppbuiltin.c	(revision 277989)
+++ gcc/c-family/c-cppbuiltin.c	(working copy)
@@ -259,11 +259,16 @@ builtin_define_float_constants (const char *name_p
   /* Since, for the supported formats, B is always a power of 2, we
      construct the following numbers directly as a hexadecimal
      constants.  */
-  get_max_float (fmt, buf, sizeof (buf));
+  get_max_float (fmt, buf, sizeof (buf), false);
 
   sprintf (name, "__%s_MAX__", name_prefix);
   builtin_define_with_hex_fp_value (name, type, decimal_dig, buf, fp_suffix, fp_cast);
 
+  get_max_float (fmt, buf, sizeof (buf), true);
+
+  sprintf (name, "__%s_NORM_MAX__", name_prefix);
+  builtin_define_with_hex_fp_value (name, type, decimal_dig, buf, fp_suffix, fp_cast);
+
   /* The minimum normalized positive floating-point number,
      b**(emin-1).  */
   sprintf (name, "__%s_MIN__", name_prefix);
@@ -1607,10 +1612,10 @@ struct GTY(()) lazy_hex_fp_value_struct
 };
 /* Number of the expensive to compute macros we should evaluate lazily.
    Each builtin_define_float_constants invocation calls
-   builtin_define_with_hex_fp_value 4 times and builtin_define_float_constants
+   builtin_define_with_hex_fp_value 5 times and builtin_define_float_constants
    is called for FLT, DBL, LDBL and up to NUM_FLOATN_NX_TYPES times for
    FLTNN*.  */ 
-#define LAZY_HEX_FP_VALUES_CNT (4 * (3 + NUM_FLOATN_NX_TYPES))
+#define LAZY_HEX_FP_VALUES_CNT (5 * (3 + NUM_FLOATN_NX_TYPES))
 static GTY(()) struct lazy_hex_fp_value_struct
   lazy_hex_fp_values[LAZY_HEX_FP_VALUES_CNT];
 static GTY(()) unsigned lazy_hex_fp_value_count;
Index: gcc/d/d-target.cc
===================================================================
--- gcc/d/d-target.cc	(revision 277989)
+++ gcc/d/d-target.cc	(working copy)
@@ -82,7 +82,7 @@ define_float_constants (tree type)
   const real_format *fmt = REAL_MODE_FORMAT (mode);
 
   /* The largest representable value that's not infinity.  */
-  get_max_float (fmt, buf, sizeof (buf));
+  get_max_float (fmt, buf, sizeof (buf), false);
   real_from_string (&T::max.rv (), buf);
 
   /* The smallest representable normalized value that's not 0.  */
Index: gcc/ginclude/float.h
===================================================================
--- gcc/ginclude/float.h	(revision 277989)
+++ gcc/ginclude/float.h	(working copy)
@@ -238,6 +238,18 @@ see the files COPYING3 and COPYING.RUNTIME respect
 
 #endif /* C11 */
 
+#if defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L
+/* Maximum finite positive value with MANT_DIG digits in the
+   significand taking their maximum value.  */
+#undef FLT_NORM_MAX
+#undef DBL_NORM_MAX
+#undef LDBL_NORM_MAX
+#define FLT_NORM_MAX	__FLT_NORM_MAX__
+#define DBL_NORM_MAX	__DBL_NORM_MAX__
+#define LDBL_NORM_MAX	__LDBL_NORM_MAX__
+
+#endif /* C2X */
+
 #ifdef __STDC_WANT_IEC_60559_BFP_EXT__
 /* Number of decimal digits for which conversions between decimal
    character strings and binary formats, in both directions, are
Index: gcc/real.c
===================================================================
--- gcc/real.c	(revision 277989)
+++ gcc/real.c	(working copy)
@@ -5242,13 +5242,19 @@ real_nextafter (REAL_VALUE_TYPE *r, format_helper
 /* Write into BUF the maximum representable finite floating-point
    number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
    float string.  LEN is the size of BUF, and the buffer must be large
-   enough to contain the resulting string.  */
+   enough to contain the resulting string.  If NORM_MAX, instead write
+   the maximum representable finite normalized floating-point number,
+   defined to be such that all choices of digits for that exponent are
+   representable in the format (this only makes a difference for IBM
+   long double).  */
 
 void
-get_max_float (const struct real_format *fmt, char *buf, size_t len)
+get_max_float (const struct real_format *fmt, char *buf, size_t len,
+	       bool norm_max)
 {
   int i, n;
   char *p;
+  bool is_ibm_extended = fmt->pnan < fmt->p;
 
   strcpy (buf, "0x0.");
   n = fmt->p;
@@ -5256,8 +5262,9 @@ void
     *p++ = 'f';
   if (i < n)
     *p++ = "08ce"[n - i];
-  sprintf (p, "p%d", fmt->emax);
-  if (fmt->pnan < fmt->p)
+  sprintf (p, "p%d",
+	   (is_ibm_extended && norm_max) ? fmt->emax - 1 : fmt->emax);
+  if (is_ibm_extended && !norm_max)
     {
       /* This is an IBM extended double format made up of two IEEE
 	 doubles.  The value of the long double is the sum of the
Index: gcc/real.h
===================================================================
--- gcc/real.h	(revision 277989)
+++ gcc/real.h	(working copy)
@@ -524,7 +524,7 @@ extern bool real_nextafter (REAL_VALUE_TYPE *, for
 /* Write into BUF the maximum representable finite floating-point
    number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
    float string.  BUF must be large enough to contain the result.  */
-extern void get_max_float (const struct real_format *, char *, size_t);
+extern void get_max_float (const struct real_format *, char *, size_t, bool);
 
 #ifndef GENERATOR_FILE
 /* real related routines.  */
Index: gcc/testsuite/gcc.dg/c11-float-3.c
===================================================================
--- gcc/testsuite/gcc.dg/c11-float-3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/c11-float-3.c	(working copy)
@@ -0,0 +1,17 @@
+/* Test *_NORM_MAX not defined for C11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+#include <float.h>
+
+#ifdef FLT_NORM_MAX
+#error "FLT_NORM_MAX defined"
+#endif
+
+#ifdef DBL_NORM_MAX
+#error "DBL_NORM_MAX defined"
+#endif
+
+#ifdef LDBL_NORM_MAX
+#error "LDBL_NORM_MAX defined"
+#endif
Index: gcc/testsuite/gcc.dg/c2x-float-1.c
===================================================================
--- gcc/testsuite/gcc.dg/c2x-float-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/c2x-float-1.c	(working copy)
@@ -0,0 +1,37 @@
+/* Test *_NORM_MAX macros.  */
+/* { dg-do run } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+#include <float.h>
+
+#ifndef FLT_NORM_MAX
+#error "FLT_NORM_MAX undefined"
+#endif
+
+#ifndef DBL_NORM_MAX
+#error "DBL_NORM_MAX undefined"
+#endif
+
+#ifndef LDBL_NORM_MAX
+#error "LDBL_NORM_MAX undefined"
+#endif
+
+extern void abort (void);
+extern void exit (int);
+
+int
+main (void)
+{
+  if (FLT_NORM_MAX != FLT_MAX)
+    abort ();
+  if (DBL_NORM_MAX != DBL_MAX)
+    abort ();
+#if LDBL_MANT_DIG == 106
+  if (LDBL_NORM_MAX != 0x0.ffffffffffffffffffffffffffcp1023L)
+    abort ();
+#else
+  if (LDBL_NORM_MAX != LDBL_MAX)
+    abort ();
+#endif
+  exit (0);
+}
