On 5/24/26 4:16 PM, Jerry D wrote:
On 5/24/26 3:23 PM, Thomas Koenig wrote:
Jerry,

a quick question: We have GFC_UINTEGER_8, would it make sense to use
that?  That could get you out of the C type quagmire.

Best regards

     Thomas


This is a good idea. I think I will try this in a later patch as a cleanup. There may be other places where ought to be used.

Cheers,

Jerry



I revised the patch to use GFC_UINTEGER_8 in all places. It regression tests clean. This should be good to go. See attached.

Thanks for the feedback.

Jerry
From 0fa89b926ed0e2227fddb7b250fcf16e61dfe072 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Sun, 24 May 2026 11:51:49 -0700
Subject: [PATCH] Fortran: [PR93727] Fix EX format kind=8 output on ILP32
 targets

On 32-bit targets such as ARM where unsigned long is 32 bits, the
kind=8 case in get_float_hex_string used unsigned long for frac_part.
The kind=8 mantissa requires 52 bits (13 hex digits), so the cast
silently truncated the upper bits, producing wrong hex output.  In
addition, converting a ~4.5e15 double value to a 32-bit unsigned long
is out of range, which raised IEEE_INVALID_FLAG on ARM hardware.

Fix kind=8 by using GFC_UINTEGER_8, which is guaranteed 64 bits on all
targets.  Update kind=10 and kind=16 to use GFC_UINTEGER_8 throughout
for consistency with libgfortran conventions.

	PR fortran/93727

libgfortran/ChangeLog:

	* io/write_float.def (get_float_hex_string): Fix kind=8 frac_part
	from unsigned long to GFC_UINTEGER_8 to correct truncated hex
	output and IEEE_INVALID_FLAG on ILP32 targets.  Update kind=10
	and kind=16 to use GFC_UINTEGER_8 for consistency.

Assisted by: Claude Sonnet 4.6
---
 libgfortran/io/write_float.def | 34 ++++++++++++++++++++--------------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/libgfortran/io/write_float.def b/libgfortran/io/write_float.def
index 608106af7c7..da31c8af51c 100644
--- a/libgfortran/io/write_float.def
+++ b/libgfortran/io/write_float.def
@@ -128,7 +128,7 @@ get_float_hex_string (const void *source, int kind, char *buffer,
 	double mant;
 	int expon;
 	int int_part;
-	unsigned long frac_part;
+	GFC_UINTEGER_8 frac_part;
 
 	val = *(const GFC_REAL_8 *) source;
 	is_negative = signbit (val);
@@ -172,11 +172,13 @@ get_float_hex_string (const void *source, int kind, char *buffer,
 	int_part = (int) mant;
 	/* 56 is the nearest integer divisible by 4 that is >= 53 (mantissa bits
 	   for kind=8).  (56-4)/4 = 13 hex digits for the fractional part.  */
-	frac_part = (unsigned long) scalbn (mant - (double) int_part, 56 - 4);
+	frac_part = (GFC_UINTEGER_8) scalbn (mant - (double) int_part, 56 - 4);
 	if (is_negative)
-	  result = snprintf (buffer, 25, "-0X%X.%13.13lXP%+d", int_part, frac_part, expon);
+	  result = snprintf (buffer, 25, "-0X%X.%13.13llXP%+d", int_part,
+			     (GFC_UINTEGER_8) frac_part, expon);
 	else
-	  result = snprintf (buffer, 25, "0X%X.%13.13lXP%+d", int_part, frac_part, expon);
+	  result = snprintf (buffer, 25, "0X%X.%13.13llXP%+d", int_part,
+			     (GFC_UINTEGER_8) frac_part, expon);
       }
       break;
 #ifdef HAVE_GFC_REAL_10
@@ -186,7 +188,7 @@ get_float_hex_string (const void *source, int kind, char *buffer,
 	GFC_REAL_10 mant;
 	int expon;
 	int int_part;
-	unsigned long long frac_part;
+	GFC_UINTEGER_8 frac_part;
 
 	val = *(const GFC_REAL_10 *) source;
 	is_negative = signbit (val);
@@ -230,11 +232,13 @@ get_float_hex_string (const void *source, int kind, char *buffer,
 	int_part = (int) mant;
 	/* 64 is the nearest integer divisible by 4 that is >= 64 (mantissa bits
 	   for kind=10).  (64-4)/4 = 15 hex digits for the fractional part.  */
-	frac_part = (unsigned long long) scalbnl (mant - (GFC_REAL_10) int_part, 64 - 4);
+	frac_part = (GFC_UINTEGER_8) scalbnl (mant - (GFC_REAL_10) int_part, 64 - 4);
 	if (is_negative)
-	  result = snprintf (buffer, 28, "-0X%X.%15.15llXP%+d", int_part, frac_part, expon);
+	  result = snprintf (buffer, 28, "-0X%X.%15.15llXP%+d", int_part,
+			     (GFC_UINTEGER_8) frac_part, expon);
 	else
-	  result = snprintf (buffer, 28, "0X%X.%15.15llXP%+d", int_part, frac_part, expon);
+	  result = snprintf (buffer, 28, "0X%X.%15.15llXP%+d", int_part,
+			     (GFC_UINTEGER_8) frac_part, expon);
       }
       break;
 #endif
@@ -245,7 +249,7 @@ get_float_hex_string (const void *source, int kind, char *buffer,
 	GFC_REAL_16 mant;
 	int expon;
 	int int_part;
-	unsigned long long frac_hi, frac_lo;
+	GFC_UINTEGER_8 frac_hi, frac_lo;
 	GFC_REAL_16 frac_val, frac_lo_val;
 
 	val = *(const GFC_REAL_16 *) source;
@@ -291,17 +295,19 @@ get_float_hex_string (const void *source, int kind, char *buffer,
 	/* 116 is the nearest integer divisible by 4 that is >= 113 (mantissa
 	   bits for kind=16).  (116-4)/4 = 28 hex digits for the fractional
 	   part, split into two 56-bit halves (14 hex digits each) to fit in
-	   unsigned long long.  */
+	   GFC_UINTEGER_8.  */
 	frac_val = mant - (GFC_REAL_16) int_part;
-	frac_hi = (unsigned long long) GFC_REAL_16_SCALBN (frac_val, 56);
+	frac_hi = (GFC_UINTEGER_8) GFC_REAL_16_SCALBN (frac_val, 56);
 	frac_lo_val = frac_val - GFC_REAL_16_SCALBN ((GFC_REAL_16) frac_hi, -56);
-	frac_lo = (unsigned long long) GFC_REAL_16_SCALBN (frac_lo_val, 112);
+	frac_lo = (GFC_UINTEGER_8) GFC_REAL_16_SCALBN (frac_lo_val, 112);
 	if (is_negative)
 	  result = snprintf (buffer, 42, "-0X%X.%14.14llX%14.14llXP%+d",
-			     int_part, frac_hi, frac_lo, expon);
+			     int_part, (GFC_UINTEGER_8) frac_hi,
+			     (GFC_UINTEGER_8) frac_lo, expon);
 	else
 	  result = snprintf (buffer, 42, "0X%X.%14.14llX%14.14llXP%+d",
-			     int_part, frac_hi, frac_lo, expon);
+			     int_part, (GFC_UINTEGER_8) frac_hi,
+			     (GFC_UINTEGER_8) frac_lo, expon);
       }
       break;
 #endif /* HAVE_GFC_REAL_16  */
-- 
2.54.0

Reply via email to