Hi, Steve Ellcey reported that gfortran.dg/pr19155.f fails on HP-UX 11.31 because on that target strtod sets errno to EINVAL for invalid input, whereas many other implementations such as glibc does not.
The problem is that C99 says nothing about setting errno to EINVAL for invalid input, and POSIX merely says that "errno may be set to [EINVAL]." http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtod.html Therefore the portable way of checking for bad input is to check whether endptr == nptr instead of checking whether errno == EINVAL. The attached patch does this. In order to support the extension of parsing an input of style "E+NN" as 0, the patch fixes the string passed to strtod. This is done only when using an edit descriptor, not for list input, as with list input there's IMHO a bigger chance we accidentally parse the wrong thing if we accept stuff like this. Regtested on x86_64-unknown-linux-gnu, Ok for trunk? 2011-05-28 Janne Blomqvist <j...@gcc.gnu.org> PR libfortran/19155 * io/read.c (convert_real): Check for invalid input by comparing endptr instead of EINVAL. (read_f): Fixup floating point input without significand. -- Janne Blomqvist
diff --git a/libgcc/configure b/libgcc/configure old mode 100644 new mode 100755 diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c index 3ee5717..d07d09d 100644 --- a/libgfortran/io/read.c +++ b/libgfortran/io/read.c @@ -131,45 +131,45 @@ max_value (int length, int signed_flag) /* convert_real()-- Convert a character representation of a floating - point number to the machine number. Returns nonzero if there is a - range problem during conversion. Note: many architectures - (e.g. IA-64, HP-PA) require that the storage pointed to by the dest - argument is properly aligned for the type in question. */ + point number to the machine number. Returns nonzero if there is an + invalid input. Note: many architectures (e.g. IA-64, HP-PA) + require that the storage pointed to by the dest argument is + properly aligned for the type in question. */ int convert_real (st_parameter_dt *dtp, void *dest, const char *buffer, int length) { - errno = 0; + char *endptr = NULL; switch (length) { case 4: *((GFC_REAL_4*) dest) = #if defined(HAVE_STRTOF) - gfc_strtof (buffer, NULL); + gfc_strtof (buffer, &endptr); #else - (GFC_REAL_4) gfc_strtod (buffer, NULL); + (GFC_REAL_4) gfc_strtod (buffer, &endptr); #endif break; case 8: - *((GFC_REAL_8*) dest) = gfc_strtod (buffer, NULL); + *((GFC_REAL_8*) dest) = gfc_strtod (buffer, &endptr); break; #if defined(HAVE_GFC_REAL_10) && defined (HAVE_STRTOLD) case 10: - *((GFC_REAL_10*) dest) = gfc_strtold (buffer, NULL); + *((GFC_REAL_10*) dest) = gfc_strtold (buffer, &endptr); break; #endif #if defined(HAVE_GFC_REAL_16) # if defined(GFC_REAL_16_IS_FLOAT128) case 16: - *((GFC_REAL_16*) dest) = __qmath_(strtoflt128) (buffer, NULL); + *((GFC_REAL_16*) dest) = __qmath_(strtoflt128) (buffer, &endptr); break; # elif defined(HAVE_STRTOLD) case 16: - *((GFC_REAL_16*) dest) = gfc_strtold (buffer, NULL); + *((GFC_REAL_16*) dest) = gfc_strtold (buffer, &endptr); break; # endif #endif @@ -178,10 +178,10 @@ convert_real (st_parameter_dt *dtp, void *dest, const char *buffer, int length) internal_error (&dtp->common, "Unsupported real kind during IO"); } - if (errno == EINVAL) + if (buffer == endptr) { generate_error (&dtp->common, LIBERROR_READ_VALUE, - "Error during floating point read"); + "Error during floating point read"); next_record (dtp, 1); return 1; } @@ -1114,6 +1114,14 @@ done: /* Output a trailing '0' after decimal point if not yet found. */ if (seen_dp && !seen_dec_digit) *(out++) = '0'; + /* Handle input of style "E+NN" by inserting a 0 for the + significand. */ + else if (!seen_int_digit && !seen_dec_digit) + { + notify_std (&dtp->common, GFC_STD_LEGACY, + "REAL input of style 'E+NN'"); + *(out++) = '0'; + } /* Print out the exponent to finish the reformatted number. Maximum 4 digits for the exponent. */