Patchwork [libfortran] PR43298 Fortran library does not read in NaN -Inf or Inf

login
register
mail settings
Submitter Jerry DeLisle
Date June 27, 2010, 7:40 p.m.
Message ID <4C27A929.8010702@verizon.net>
Download mbox | patch
Permalink /patch/57099/
State New
Headers show

Comments

Jerry DeLisle - June 27, 2010, 7:40 p.m.
This attached patch adds code to read these special cases.  The code is self 
explanatory. This added feature is for formatted READ.

As a follow-up patch we can consider the contents of the parenthesis with NAN() 
and explicitly set the result. I will also be looking at how to handle signs.

Regression tested on x86-64. testing on other platforms would be appreciated.

OK for trunk?

Regards,

Jerry

2010-06-27  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

	PR libfortran/43298
	* io/read.c: Add code to parse and read Inf, Infinity, NaN, and Nan with
	optional parenthesis.
Tobias Burnus - June 27, 2010, 8:41 p.m.
Jerry DeLisle wrote:
> This attached patch adds code to read these special cases. The code is
> self explanatory. This added feature is for formatted READ.

Can you also add as follow up support for the parentheses to list
directed read (parse_real)? Your patch only handles "read_f".

Example (only tested with an unpatched gfortran as bootstrapping takes a
while):

!------------------------------
character(len=200) :: str
real :: r
str = 'NAN'
read(str,*) r ! OK before your patch
print *, 'Result: ',r
read(str,'(g10.5)') r ! OK with your patch
print *, 'Result: ',r

str = 'NAN(0x34)'
read(str,*) r ! FAILS (independent of your patch)
print *, 'Result: ',r
read(str,'(g10.5)') r ! OK with your patch
print *, 'Result: ',r
end
!------------------------------

> As a follow-up patch we can consider the contents of the parenthesis
> with NAN() and explicitly set the result. I will also be looking at
> how to handle signs.

I personally do not care much about the sign of NaN or the "payload"
(i.e. the bits are non-zero in the trailing significand field of the
NaN) . However, I really miss support for negative infinity. (For
list-directed I/O, it works.)

Regarding NAN("payload"): Can't we simply put the whole string to
"strtod" - including sign and parentheses? At least POSIX (IEEE Std
1003.1:2003) has for strtod/stdtof/strtold:

"The expected form of the subject sequence is an optional plus or minus
sign, then one of the following: [...]
* One of INF or INFINITY, ignoring case
* One of NAN or NAN(n-char-sequence_opt), ignoring case in the NAN part,
where:
n-char-sequence:
digit
nondigit
n-char-sequence digit
n-char-sequence nondigit"

I have no idea whether all libc handle it - if not, one could add a
configure check and truncate NAN conditionally. But may first assume
that all support it and and - if not - one can still add the check.

Regarding your patch: I wonder whether one should check your patch to
disallow spaces in the parentheses. The standard has (cf. comment 3 in
PR43298): "an optional sign, followed by the string ’NAN’, optionally
followed by zero or more alphanumeric characters enclosed in parentheses,"
A space is not an alphanumeric character. Currently, you explicitly
allow spaces in parentheses

How about checking for the parentheses also for INF - and rejecting them
if present? For instance by using a three-state seen_paren; that way one
might also reject "NAN(a)(b)" - though we should try to be fast as INF
and NAN can be common while NAN(string) or invalid INF/NAN constructs
are probably the exception.

Note: The test case needs to use
! { dg-add-options ieee }

Tobias

Patch

Index: read.c
===================================================================
--- read.c	(revision 161472)
+++ read.c	(working copy)
@@ -810,6 +810,60 @@  read_f (st_parameter_dt *dtp, const fnode *f, char
   if (w == 0)
     goto zero;
 
+
+  /* Check for Infinity or NaN.  */    
+  if (unlikely ((w >= 3 && (*p == 'i' || *p == 'I' || *p == 'n' || *p == 'N'))))
+    {
+      int i;
+      int seen_space = 0;
+      int seen_paren = 0;
+
+      /* Scan through the buffer keeping track of spaces and parenthesis. We
+	 null terminate the string as soon as we see a left paren or if we are
+	 BLANK_NULL mode.  Leading spaces have already been skipped above,
+	 trailing spaces spaces are ignored by converrting to '\0'. A space
+	 between "NaN" and the optional perenthesis is not permitted.  */
+      for (i = 0; i < w; i++)
+	{
+	  buffer[i] = tolower (p[i]);
+	  if (!seen_paren && seen_space && buffer[i] != ' ')
+	    goto bad_float;
+	  if (buffer[i] == '(')
+	    {
+	      seen_paren = 1;
+	      buffer[i] = '\0';
+	    }
+	  if (buffer[i] == ')')
+	    {
+	      if (!seen_paren)
+	        goto bad_float;
+	      else
+	        seen_paren = 0;
+	    }
+	  if (buffer[i] == ' ' && !seen_paren)
+	    {
+	      seen_space = 1;
+	      if (dtp->u.p.blank_status == BLANK_ZERO)
+		buffer[i] = '0';
+	      else /* Ignore leading and trailing blanks.  */
+	      	buffer[i] = '\0';
+	    }
+	}
+	 
+      buffer[w] = '\0';
+
+      /* If we did not see a closing parenthesis, we have a bad float.  */
+      if (seen_paren)
+        goto bad_float;
+      if (strcmp (buffer, "inf") != 0
+	  && strcmp (buffer, "nan") != 0
+	  && strcmp (buffer, "infinity") != 0)
+	goto bad_float;
+
+      convert_real (dtp, dest, buffer, length);
+      return;
+    }
+
   /* Process the mantissa string.  */
   while (w > 0)
     {