From: Message-Id: <200303221132.h2MBWDc22095@speedy.ludd.luth.se> Subject: strtof()'s NaN and Inf support To: DJGPP-WORKERS Date: Sat, 22 Mar 2003 12:32:13 +0100 (CET) X-Mailer: ELM [version 2.4ME+ PL78 (25)] MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII X-MailScanner: Found to be clean Reply-To: djgpp-workers AT delorie DOT com Hello. Here's a patch for NaN and Inf support in strtof(). Read the part documentation part carefully. The text about SNaN -> QNaN is based on the following results from running t-strtof: strtof("nan(0x401234)", &endptr) -> NaN, 13 - OK strtof("-nan(0x400088)", &endptr) -> NaN, 14 - OK strtof("nan(0x1234)", &endptr) -> NaN, 11 - mantissa == 0x401234 != 0x1234 - FAIL strtof("-nan(0x88)", &endptr) -> NaN, 10 - mantissa == 0x400088 != 0x88 - FAIL I'll change the test case so it won't check this particular "failure" after we are content with the wording. (This failure won't happen if the FPU is emulated which will be discussed in another mail because there are other failures then.) Right, MartinS Index: djgpp/src/libc/c99/stdlib/strtof.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/c99/stdlib/strtof.c,v retrieving revision 1.2 diff -p -u -r1.2 strtof.c --- djgpp/src/libc/c99/stdlib/strtof.c 23 Jan 2003 19:53:02 -0000 1.2 +++ djgpp/src/libc/c99/stdlib/strtof.c 22 Mar 2003 11:13:56 -0000 @@ -5,12 +5,15 @@ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */ +#include #include #include #include #include #include +#include #include +#include float strtof(const char *s, char **sret) @@ -43,6 +46,73 @@ strtof(const char *s, char **sret) s++; } + /* Handle INF and INFINITY. */ + if ( ! strnicmp( "INF", s, 3 ) ) + { + if( sret ) + { + if ( ! strnicmp( "INITY", &s[3], 5 ) ) + { + *sret = unconst((&s[8]), char *); + } + else + { + *sret = unconst((&s[3]), char *); + } + } + + if( 0 <= sign ) + { + return INFINITY; + } + else + { + return -INFINITY; + } + } + + /* Handle NAN and NAN(). */ + if ( ! strnicmp( "NAN", s, 3 ) ) + { + float f = NAN; + float_t n = *(float_t *)(&f); + + if ( sign < 0 ) + { + n.sign = 1; + } + + if ( s[3] == '(' ) + { + long mantissa_bits = 0; + char *endptr = unconst((&s[4]), char *); + + mantissa_bits = strtol(&s[4], &endptr, 16); + if ( *endptr == ')' ) + { + if ( mantissa_bits ) + { + n.mantissa = mantissa_bits & 0x7fffff; + } + if ( sret ) + { + *sret = endptr+1; + } + return *(float *)(&n); + } + + /* The subject sequence didn't match NAN(), so match + only NAN. */ + } + + if( sret ) + { + *sret = unconst((&s[3]), char *); + } + return *(float *)(&n); + } + + /* Handle ordinary numbers. */ while ((*s >= '0') && (*s <= '9')) { flags |= 1; Index: djgpp/src/libc/c99/stdlib/strtof.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/c99/stdlib/strtof.txh,v retrieving revision 1.4 diff -p -u -r1.4 strtof.txh --- djgpp/src/libc/c99/stdlib/strtof.txh 29 Jan 2003 12:34:24 -0000 1.4 +++ djgpp/src/libc/c99/stdlib/strtof.txh 22 Mar 2003 11:13:56 -0000 @@ -11,7 +11,9 @@ float strtof(const char *s, char **endp) @subheading Description This function converts as many characters of @var{s} as look like a -floating point number into that number. If @var{endp} is not a null +floating point number into that number. It also recognises +(case-insensitivly) @code{Inf}, @code{Infinity}, @code{NaN} and +@code{NaN(@var{optional hex-number}}. If @var{endp} is not a null pointer, @code{*endp} is set to point to the first unconverted character. @@ -19,12 +21,37 @@ character. The value the represented by @var{s}. +If @var{s} is ``Inf'' or ``Infinity'', with any variations of case and +optionally prefixed with ``+'' or ``-'', the return value is +@code{INFINITY} (if no prefix or a ``+'' prefix) or @code{-INFINITY} +(if the prefix is ``-''). + +If @var{s} is ``NaN'' or ``NaN()'', with any variations of case and +optionally prefixed with ``+'' or ``-'', the return value is +@code{NAN}. If the prefix is ``-'' the sign bit in the NaN will be +set to 1. + +If @var{s} is ``NaN(@var{hex-number})'', with any variations of case +and optionally prefixed with ``+'' or ``-'', the return value is a NaN +with the mantissa bits set to @code{@var{hex-number}&0x7fffff} (the +mantissa for floats consists of 23 bits). Use at most 8 hexadecimal +digits in @var{hex-number} or the internal conversion will overflow, +which results in a mantissa of 7fffff. If +@code{@var{hex-number}&0x7fffff} is 0 (which won't work as a +representation of a NaN) @code{NAN} will be returned. If the prefix +is ``-'' the sign bit in the NaN will be set to 1. Testing shows that +SNaNs might be converted into QNaNs (most significant bit will be set +in the mantissa). + If a number represented by @var{s} doesn't fit into the range of values representable by the type @code{float}, the function returns either @code{-HUGE_VALF} (if @var{s} begins with the character @code{-}) or @code{+HUGE_VALF}, and sets @code{errno} to @code{ERANGE}. @subheading Portability + +@port-note ansi-c99 Support for ``Inf'', ``Infinity'', ``NaN'' and +``NaN(@dots{})'' was standardised in ANSI C99. @portability !ansi-c89, ansi-c99, !posix-1003.2-1992, posix-1003.1-2001 Index: djgpp/tests/libc/c99/stdlib/t-strtof.c =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/c99/stdlib/t-strtof.c,v retrieving revision 1.2 diff -p -u -r1.2 t-strtof.c --- djgpp/tests/libc/c99/stdlib/t-strtof.c 23 Jan 2003 19:53:57 -0000 1.2 +++ djgpp/tests/libc/c99/stdlib/t-strtof.c 22 Mar 2003 11:14:03 -0000 @@ -51,6 +51,68 @@ static const test3_t tests3[] = { static const size_t n_tests3 = sizeof(tests3) / sizeof(tests3[0]); +typedef struct { + const char * const str; /* String to run strtof() on. */ + const int diff; /* For endptr tests. How many characters from string start + endptr should be offset. */ +} test4_t; + +static const test4_t tests4[] = { + { "inF", 3 }, + { "-INf", 4 }, + { "infi", 3 }, + { "-infi", 4 }, + { "infinit", 3 }, + { "-infinit", 4 }, + { "INfINITY", 8 }, + { "-InfInIty", 9 }, + { "infinity0", 8 }, + { "-infinity5", 9 }, + { "infinity-1", 8 }, + { "-infinity-6", 9 }, +}; + +static const size_t n_tests4 = sizeof(tests4) / sizeof(tests4[0]); + +typedef struct { + const char * const str; /* String to run strtof() on. */ + const int diff; /* For endptr tests. How many characters from string start + endptr should be offset. */ + const int sign; /* Sign bit. */ + const int mantissa; /* What mantissa should be set to after + conversion. 0 -> don't care as long as it's + non-zero. */ +} test5_t; + +static const test5_t tests5[] = { + { "nAn", 3, 0, 0 }, + { "-nAn", 4, 1, 0 }, + { "nanny", 3, 0, 0 }, + { "-nanny", 4, 1, 0 }, + { "NAN()", 5, 0, 0 }, + { "NAN( )", 3, 0, 0 }, + { "-Nan()", 6, 1, 0 }, + { "nAN(0)", 6, 0, 0 }, + { "-nan(0)", 7, 1, 0 }, + { "nan(0x401234)", 13, 0, 0x401234 }, /* QNaN */ + { "-nan(0x400088)", 14, 1, 0x400088 }, /* QNaN */ + { "nan(0x1234)", 11, 0, 0x1234 }, /* Don't check: SNaN -> QNaN. */ + { "-nan(0x88)", 10, 1, 0x88 }, /* Don't check: SNaN -> QNaN. */ + { "-nan(0xaa7d7aa74)", 17, 1, 0 }, /* Don't check: overflow. */ + { "nan(0x12345678123456781)", 24, 0, 0 }, /* Don't check: overflow. */ + { "-nan(0x12345678123456781)", 25, 1, 0 }, /* Don't check: overflow. */ + { "naN(something)", 3, 0, 0 }, + { "-nAn(smurf)", 4, 1, 0 }, + { "-nan(-nan)", 4, 1, 0 }, + { "nan(nan(nan()))", 3, 0, 0 }, + { "NaN(0x1234oops)", 3, 0, 0 }, + { "nan()()", 5, 0, 0 }, + { "NAN()nan", 5, 0, 0 }, +}; + +static const size_t n_tests5 = sizeof(tests5) / sizeof(tests5[0]); + + static void inline result (const size_t n, const float f_in, const float f_out) { @@ -106,6 +168,60 @@ main (void) puts("No - OK"); } } - + + puts("Infinity tests:"); + for (i = 0; i < n_tests4; i++) { + char *endptr; + float_t float_bits; + + f_res = strtof(tests4[i].str, &endptr); + + printf("strtof(\"%s\", &endptr) -> %f, %ld - ", tests4[i].str, + f_res, endptr-tests4[i].str); + + /* Need to do the Inf detection ourselves. */ + float_bits = *(float_t *)(&f_res); + if (float_bits.exponent != 0xff || float_bits.mantissa != 0) { + puts("exponent != 0xff or mantissa == 0 - FAIL"); + } else if ( (float_bits.sign && 0 < f_res ) || + (!float_bits.sign && f_res < 0) ) { + puts("Wrong sign - FAIL"); + } else if ( endptr-tests4[i].str != tests4[i].diff) { + printf("endptr-(start_of_string) == %ld != %d - FAIL\n", + endptr-tests4[i].str, tests4[i].diff); + } else { + puts("OK"); + } + } + + puts("Nan tests:"); + for (i = 0; i < n_tests5; i++) { + char *endptr; + float_t float_bits; + + f_res = strtof(tests5[i].str, &endptr); + + printf("strtof(\"%s\", &endptr) -> %f, %ld - ", tests5[i].str, + f_res, endptr-tests5[i].str); + + /* Need to to the NaN detection ourselves. */ + float_bits = *(float_t *)(&f_res); + if (float_bits.exponent != 0xff || float_bits.mantissa == 0) { + puts("exponent != 0xff or mantissa == 0 - FAIL"); + } else if (tests5[i].mantissa && + tests5[i].mantissa != float_bits.mantissa) { + printf("mantissa == 0x%x != 0x%x - FAIL\n", + float_bits.mantissa, tests5[i].mantissa); + } else if (tests5[i].sign != float_bits.sign) { + printf("sign == %d != %d - FAIL\n", float_bits.sign, + tests5[i].sign); + } else if ( endptr-tests5[i].str != tests5[i].diff) { + printf("endptr-(start_of_string) == %ld != %d - FAIL\n", + endptr-tests5[i].str, tests5[i].diff); + } else { + puts("OK"); + } + } + return(EXIT_SUCCESS); }