X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f X-Recipient: djgpp-workers AT delorie DOT com X-Authenticated: #27081556 X-Provags-ID: V01U2FsdGVkX1/FDgxEV6bFroPYVihZTfiX5T5WTDShXrZBdJvIoJ UV1mfHWfsjgBOe From: Juan Manuel Guerrero To: djgpp-workers AT delorie DOT com Subject: Re: Hex float string conversion for strto[dfld] Date: Thu, 10 Apr 2008 23:30:39 +0200 User-Agent: KMail/1.9.5 References: <200804082232 DOT 28045 DOT juan DOT guerrero AT gmx DOT de> <200804082050 DOT m38KoI1P003051 AT envy DOT delorie DOT com> In-Reply-To: <200804082050.m38KoI1P003051@envy.delorie.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200804102330.40487.juan.guerrero@gmx.de> X-Y-GMX-Trusted: 0 Reply-To: djgpp-workers AT delorie DOT com Am Dienstag, 8. April 2008 22:50 schrieben Sie: > > Please add suitable tests to the test for these, and if it all passes, > it's OK. I have written a small test program to test a new version of strtold. This is the output it produces: Integers. test string: <0x1 ###> endp: < ###> value: 1.000000e+00 test string: <0x2 ###> endp: < ###> value: 2.000000e+00 test string: <0xF ###> endp: < ###> value: 1.500000e+01 test string: <0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ###> endp: < ###> value: 5.146315e+74 Zeros test string: <0x0 ###> endp: < ###> value: 0.000000e+00 test string: <0x0. ###> endp: < ###> value: 0.000000e+00 test string: <0x.0 ###> endp: < ###> value: 0.000000e+00 test string: <0x000.000 ###> endp: < ###> value: 0.000000e+00 test string: <0x0.0P-00###> endp: <###> value: 0.000000e+00 test string: <0x0.0P+00###> endp: <###> value: 0.000000e+00 test string: <0x0.P-00###> endp: <###> value: 0.000000e+00 test string: <0x.0P-00###> endp: <###> value: 0.000000e+00 Floating points. test string: <0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+123###> endp: <###> value: 1.533599e-33 test string: <0x0.0P+9999999999999999###> endp: <###> value: 0.000000e+00 test string: <0x1. ###> endp: < ###> value: 1.000000e+00 test string: <0x1.P00###> endp: <###> value: 1.000000e+00 test string: <0x1P+0###> endp: <###> value: 1.000000e+00 test string: <0x.1P-0###> endp: <###> value: 6.250000e-02 test string: <0x12345678.9ABCDEF1p+12345 ###> endp: < ###> value: 5.014109e+3724 test string: <0xF.EDcba ###> endp: < ###> value: 1.592889e+01 test string: <0x123456789abcdef.fedcba987654321 ###> endp: < ###> value: 8.198553e+16 Overflow and underflow errors. test string: <0x1.0P+9999999999999999###> endp: <###> value: Inf ERANGE test string: <0x7.8P+123456789###> endp: <###> value: Inf ERANGE test string: <0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+987654321###> endp: <###> value: Inf ERANGE test string: <0x1.0P-9999999999999999###> endp: <###> value: 0.000000e+00 ERANGE test string: <0x7.8P-123456789###> endp: <###> value: 0.000000e+00 ERANGE test string: <0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P-987654321###> endp: <###> value: 0.000000e+00 ERANGE Parsing errors. test string: <0x 123 ###> endp: < 123 ###> value: 0.000000e+00 EINVAL test string: <0x123. 123 ###> endp: < 123 ###> value: 2.910000e+02 test string: <0x123.123 p123 ###> endp: < p123 ###> value: 2.910710e+02 test string: <0x123.123p 123 ###> endp:

value: 2.910710e+02 If I have missed some test case, please let me know. There are 3 versions of the program with different value ranges to test strto[dfld]. Because my linux does not support hex floating strings I have checked against the strtod version from gnulib. Of course the test programs shall only test the new features. Regards, Juan M. Guerrero 2008-04-10 Juan Manuel Guerrero Diffs against djgpp CVS head of 2008-04-08. * src/libc/ansi/stdlib/strtold.c: Conversion of hex floating point strings of the form 0xH.HHH[P|p][+|-]DDD implemented. * src/libc/ansi/stdlib/strtold.txh: Info about hex floating point string conversion added. * src/libc/ansi/stdlib/strtod.c: Conversion of hex floating point strings of the form 0xH.HHH[P|p][+|-]DDD implemented. * src/libc/ansi/stdlib/strtod.txh: Info about hex floating point string conversion added. * src/libc/c99/stdlib/strtof.c: Conversion of hex floating point strings of the form 0xH.HHH[P|p][+|-]DDD implemented. * src/libc/c99/stdlib/strtof.txh: Info about hex floating point string conversion added. *tests/libc/ansi/stdlib/makefile: strtold.c added to the target list. *tests/libc/ansi/stdlib/strtod.c: Test cases for hex float string conversion added. *tests/libc/ansi/stdlib/strtold.c: Initial release. *tests/libc/c99/stdlib/makefile: strtof.c added to the target list. *tests/libc/c99/stdlib/strtof.c: Initial release. diff -aprNU3 djgpp.orig/src/libc/ansi/stdlib/strtod.c djgpp/src/libc/ansi/stdlib/strtod.c --- djgpp.orig/src/libc/ansi/stdlib/strtod.c 2008-04-08 17:55:48 +0000 +++ djgpp/src/libc/ansi/stdlib/strtod.c 2008-04-10 21:22:46 +0000 @@ -15,6 +15,18 @@ #include #include +#define HEX_DIGIT_SIZE (4) +#define DOUBLE_BIAS (0x3FFU) +#define MAX_BIN_EXPONENT (1023) /* Max. and min. binary exponent (inclusive) as */ +#define MIN_BIN_EXPONENT (-1022) /* defined in Intel manual (253665.pdf, Table 4.2). */ +#define IS_DEC_DIGIT(x) (((x) >= '0') && ((x) <= '9')) +#define IS_HEX_DIGIT(x) ((((x) >= 'A') && ((x) <= 'F')) || \ + (((x) >= 'a') && ((x) <= 'f')) || \ + IS_DEC_DIGIT(x)) +#define IS_EXPONENT(x) (((x[0]) == 'P' || (x[0]) == 'p') && \ + (x[1] == '+' || x[1] == '-' || IS_DEC_DIGIT(x[1]))) + + double strtod(const char *s, char **sret) { @@ -113,8 +125,170 @@ strtod(const char *s, char **sret) return (t.d); } + /* Handle 0xH.HHH[p|P][+|-]DDD. */ + if ( ! strnicmp( "0x", s, 2 ) ) + { + int digits, integer_digits; + long int bin_exponent; + unsigned long long int mantissa; + _double_union_t d_value; + + + /* + * Mantissa. + * 13 hex digits fit into the fraction part. + */ + bin_exponent = 0; + integer_digits = 0; + mantissa = 0x00ULL; + s += 2; + while (integer_digits < 16 && IS_HEX_DIGIT(*s)) + { + flags = 1; + mantissa <<= HEX_DIGIT_SIZE; + mantissa += IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + if (mantissa) /* Discarts leading zeros. */ + integer_digits++; /* Counts hex digits. 16**integer_digits. */ + s++; + } + if (integer_digits) + { + /* + * Compute the binary exponent for a normalized mantissa by + * shifting the decimal point inside the most significant hex digit. + */ + unsigned long long bit = 0x01ULL; + + digits = 0; + if (IS_HEX_DIGIT(*s)) + { + char *t = unconst(s, char *); + + for (; IS_HEX_DIGIT(*t); t++) + digits++; /* Counts hex digits. */ + } + bin_exponent = integer_digits * HEX_DIGIT_SIZE - 1; /* 2**bin_exponent. */ + for (bit <<= bin_exponent; !(mantissa & bit); bin_exponent--) + bit >>= 1; + bin_exponent += digits * HEX_DIGIT_SIZE; + } + + if (*s == decimal) + { + int fraction_zeros = 0; + + s++; + digits = integer_digits; + while ((digits - fraction_zeros) < 16 && IS_HEX_DIGIT(*s)) + { + flags = 1; + digits++; /* Counts hex digits. */ + mantissa <<= HEX_DIGIT_SIZE; + mantissa += IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + if (mantissa == 0) + fraction_zeros++; /* Counts hex zeros. 16**(-fraction_zeros + 1). */ + s++; + } + if (!integer_digits && mantissa) + { + /* + * Compute the binary exponent for a normalized mantissa by + * shifting the decimal point inside the most significant hex digit. + */ + unsigned long long bit = 0x01ULL; + + bin_exponent = -fraction_zeros * HEX_DIGIT_SIZE; /* 2**bin_exponent. */ + for (bit <<= (digits * HEX_DIGIT_SIZE + bin_exponent); !(mantissa & bit); bin_exponent--) + bit >>= 1; + } + } + + if (!flags) + { + if (sret) + *sret = unconst(s, char *); + errno = EINVAL; /* No valid mantissa, no convertion could be performed. */ + return 0.0L; + } + + if (mantissa) + { + /* + * Normalize mantissa. + */ + while (!(mantissa & 0x8000000000000000ULL)) + mantissa <<= 1; /* Shift a binary 1 into the integer part of the mantissa. */ + mantissa >>= (63 - 52); + /* At this point the mantissa is normalized and the exponent has been adjusted accordingly. */ + } + + + /* + * After discarting all hex digits left, + * if the next character is P or p + * continue with the extraction of the + * exponent, else any other character + * that have appeared terminates the number. + */ + while (IS_HEX_DIGIT(*s)) + s++; + + /* + * Exponent. + */ + if (IS_EXPONENT(s)) + { + long int exponent = 0.0; + s++; + if (*s == '+') + s++; + else if (*s == '-') + { + esign = -1; + s++; + } + + while ((esign * exponent + bin_exponent) < (MAX_BIN_EXPONENT + 1) && IS_DEC_DIGIT(*s)) + { + exponent *= 10; + exponent += *s - '0'; + s++; + } + bin_exponent += esign * exponent; /* 2**bin_exponent. */ + while (IS_DEC_DIGIT(*s)) + s++; /* Discart rest of exponent. */ + } + + + if (sret) + *sret = unconst(s, char *); + if (mantissa) + { + if (bin_exponent > MAX_BIN_EXPONENT) + { + errno = ERANGE; + return sign * HUGE_VAL; + } + else if(bin_exponent < MIN_BIN_EXPONENT) + { + errno = ERANGE; + return 0.0; + } + d_value.dt.sign = (sign == 1) ? 0 : 1; + d_value.dt.exponent = 0x7FFFU & (bin_exponent + DOUBLE_BIAS); + d_value.dt.mantissah = 0x000FFFFFUL & (mantissa >> 32); + d_value.dt.mantissal = 0xFFFFFFFFUL & mantissa; + } + else + d_value.d = sign * 0.0; + + return d_value.d; + } + /* Handle ordinary numbers. */ - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { flags |= 1; r *= 10.0; @@ -151,7 +325,7 @@ strtod(const char *s, char **sret) s++; esign = -1; } - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { e *= 10; e += *s - '0'; diff -aprNU3 djgpp.orig/src/libc/ansi/stdlib/strtod.txh djgpp/src/libc/ansi/stdlib/strtod.txh --- djgpp.orig/src/libc/ansi/stdlib/strtod.txh 2003-10-25 11:15:58 +0000 +++ djgpp/src/libc/ansi/stdlib/strtod.txh 2008-04-10 22:31:50 +0000 @@ -5,19 +5,19 @@ @example #include -double strtod(const char *s, char **endp); +double strtod(const char *@var{s}, char **@var{endp}); @end example @subheading Description -This function converts as many characters of @var{s} as look like a -floating point number into that number. It also recognises -(case-insensitively) ``Inf'', ``Infinity'', ``NaN'', -``NaN(@var{optional decimal-number})'', -``NaN(@var{optional octal-number})'' -and ``NaN(@var{optional hex-number})''. If @var{endp} is not a null -pointer, a pointer to the first unconverted character will be stored -in the location pointed to by @var{endp}. +This function converts as many characters of @var{s} that look like a +floating point number into that number. The floating point number may +also take the form of a hex floating point number (case-insensitively) +like this [+|-]0xH.HHHp[+|-]DDD. It also recognises (case-insensitively) +``Inf'', ``Infinity'', ``NaN'', ``NaN(@var{optional decimal-number})'', +``NaN(@var{optional octal-number})'' and ``NaN(@var{optional hex-number})''. +If @var{endp} is not a null pointer, a pointer to the first unconverted +character will be stored in the location pointed to by @var{endp}. @subheading Return Value @@ -66,6 +66,7 @@ char buf[] = "123ret"; char buf2[] = "0x123ret"; char buf3[] = "NAN(123)"; char buf4[] = "NAN(0x123)"; +char buf5[] = "0x1234567.89ABCDefP+123 foobar"; char *bp; double x, x2, x3, x4; @@ -73,5 +74,6 @@ x = strtod(buf, &bp); x2 = strtod(buf2, &bp); x3 = strtod(buf3, &bp); x4 = strtod(buf4, &bp); +x5 = strtod(buf5, &bp); @end example diff -aprNU3 djgpp.orig/src/libc/ansi/stdlib/strtold.c djgpp/src/libc/ansi/stdlib/strtold.c --- djgpp.orig/src/libc/ansi/stdlib/strtold.c 2003-11-08 12:19:40 +0000 +++ djgpp/src/libc/ansi/stdlib/strtold.c 2008-04-10 21:24:16 +0000 @@ -1,3 +1,4 @@ +/* Copyright (C) 2008 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ @@ -6,11 +7,23 @@ #include #include #include +#include #include #include #include #include +#define HEX_DIGIT_SIZE (4) +#define LONG_DOUBLE_BIAS (0x3FFFU) +#define MAX_BIN_EXPONENT (16383) /* Max. and min. binary exponent (inclusive) as */ +#define MIN_BIN_EXPONENT (-16382) /* defined in Intel manual (253665.pdf, Table 4.2). */ +#define IS_DEC_DIGIT(x) (((x) >= '0') && ((x) <= '9')) +#define IS_HEX_DIGIT(x) ((((x) >= 'A') && ((x) <= 'F')) || \ + (((x) >= 'a') && ((x) <= 'f')) || \ + IS_DEC_DIGIT(x)) +#define IS_EXPONENT(x) (((x[0]) == 'P' || (x[0]) == 'p') && \ + (x[1] == '+' || x[1] == '-' || IS_DEC_DIGIT(x[1]))) + static long double powten[] = { 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, 1e64L, 1e128L, 1e256L, @@ -116,8 +129,193 @@ strtold(const char *s, char **sret) return (t.ld); } + /* Handle 0xH.HHH[p|P][+|-]DDD. */ + if ( ! strnicmp( "0x", s, 2 ) ) + { + int digits, integer_digits; + long long int bin_exponent; + unsigned long long int mantissa; + _longdouble_union_t ld_value; + + + /* + * Mantissa. + * 16 hex digits fit into the mantissa + * including the explicit integer bit. + */ + bin_exponent = 0; + integer_digits = 0; + mantissa = 0x00ULL; + s += 2; + while (integer_digits < 16 && IS_HEX_DIGIT(*s)) + { + flags = 1; + mantissa <<= HEX_DIGIT_SIZE; + mantissa += IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + if (mantissa) /* Discarts leading zeros. */ + integer_digits++; /* Counts hex digits. 16**integer_digits. */ + s++; + } + if (integer_digits) + { + /* + * Compute the binary exponent for a normalized mantissa by + * shifting the decimal point inside the most significant hex digit. + */ + unsigned long long bit = 0x01ULL; + + digits = 0; + if (IS_HEX_DIGIT(*s)) + { + char *t = unconst(s, char *); + + for (; IS_HEX_DIGIT(*t); t++) + digits++; /* Counts hex digits. */ + } + bin_exponent = integer_digits * HEX_DIGIT_SIZE - 1; /* 2**bin_exponent. */ + for (bit <<= bin_exponent; !(mantissa & bit); bin_exponent--) + bit >>= 1; + bin_exponent += digits * HEX_DIGIT_SIZE; + } + + if (*s == decimal) + { + int fraction_zeros = 0; + + s++; + digits = integer_digits; + while ((digits - fraction_zeros) < 16 && IS_HEX_DIGIT(*s)) + { + flags = 1; + digits++; /* Counts hex digits. */ + mantissa <<= HEX_DIGIT_SIZE; + mantissa += IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + if (mantissa == 0) + fraction_zeros++; /* Counts hex zeros. 16**(-fraction_zeros + 1). */ + s++; + } + if (!integer_digits && mantissa) + { + /* + * Compute the binary exponent for a normalized mantissa by + * shifting the decimal point inside the most significant hex digit. + */ + unsigned long long bit = 0x01ULL; + + bin_exponent = -fraction_zeros * HEX_DIGIT_SIZE; /* 2**bin_exponent. */ + for (bit <<= (digits * HEX_DIGIT_SIZE + bin_exponent); !(mantissa & bit); bin_exponent--) + bit >>= 1; + } + } + + if (!flags) + { + if (sret) + *sret = unconst(s, char *); + errno = EINVAL; /* No valid mantissa, no convertion could be performed. */ + return 0.0L; + } + + if (mantissa) + { + /* + * Normalize mantissa. + * If there is still a valid hex character in the string + * its bits will be inserted in the LSB of the mantissa, + * else zeros will be inserted. + */ + for (digits = 0; !(mantissa & 0x8000000000000000ULL); digits++) + mantissa <<= 1; /* Shift a binary 1 into the integer part of the mantissa. */ + if (IS_HEX_DIGIT(*s)) + { + unsigned long long bits = IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + + switch (digits) + { + case 1: + mantissa |= (0x01ULL & bits >> 3); + s++; + break; + case 2: + mantissa |= (0x03ULL & bits >> 2); + s++; + break; + case 3: + mantissa |= (0x07ULL & bits >> 1); + s++; + break; + } + } + /* At this point the mantissa is normalized and the exponent has been adjusted accordingly. */ + } + + + /* + * After discarting all hex digits left, + * if the next character is P or p + * continue with the extracting of the + * exponent, else any other character + * that have appeared terminates the number. + */ + while (IS_HEX_DIGIT(*s)) + s++; + + /* + * Exponent. + */ + if (IS_EXPONENT(s)) + { + long long int exponent = 0.0L; + s++; + if (*s == '+') + s++; + else if (*s == '-') + { + esign = -1; + s++; + } + + while ((esign * exponent + bin_exponent) < (MAX_BIN_EXPONENT + 1) && IS_DEC_DIGIT(*s)) + { + exponent *= 10; + exponent += *s - '0'; + s++; + } + bin_exponent += esign * exponent; /* 2**bin_exponent. */ + while (IS_DEC_DIGIT(*s)) + s++; /* Discart rest of exponent. */ + } + + if (sret) + *sret = unconst(s, char *); + if (mantissa) + { + if (bin_exponent > MAX_BIN_EXPONENT) + { + errno = ERANGE; + return sign * HUGE_VALL; + } + else if(bin_exponent < MIN_BIN_EXPONENT) + { + errno = ERANGE; + return 0.0L; + } + ld_value.ldt.sign = (sign == 1) ? 0 : 1; + ld_value.ldt.exponent = 0x7FFFU & (bin_exponent + LONG_DOUBLE_BIAS); + ld_value.ldt.mantissah = 0xFFFFFFFFUL & (mantissa >> 32); + ld_value.ldt.mantissal = 0xFFFFFFFFUL & mantissa; + } + else + ld_value.ld = sign * 0.0L; + + return ld_value.ld; + } + /* Handle ordinary numbers. */ - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { flags |= 1; r *= 10.0L; @@ -128,7 +326,7 @@ strtold(const char *s, char **sret) if (*s == decimal) { s++; - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { flags |= 2; r *= 10.0L; @@ -141,6 +339,7 @@ strtold(const char *s, char **sret) { if (sret) *sret = unconst(s, char *); + errno = EINVAL; /* No valid mantissa, no convertion could be performed. */ return 0.0L; } @@ -154,7 +353,7 @@ strtold(const char *s, char **sret) s++; esign = -1; } - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { e *= 10; e += *s - '0'; diff -aprNU3 djgpp.orig/src/libc/ansi/stdlib/strtold.txh djgpp/src/libc/ansi/stdlib/strtold.txh --- djgpp.orig/src/libc/ansi/stdlib/strtold.txh 2003-10-25 11:15:58 +0000 +++ djgpp/src/libc/ansi/stdlib/strtold.txh 2008-04-10 22:31:50 +0000 @@ -5,26 +5,26 @@ @example #include -long double _strtold(const char *s, char **endp); +long double _strtold(const char *@var{s}, char **@var{endp}); @end example @subheading Description This function converts as many characters of @var{s} that look like a -floating point number into that number. It also recognises -(case-insensitively) ``Inf'', ``Infinity'', ``NaN'', -``NaN(@var{optional decimal-number})'', -``NaN(@var{optional octal-number})'' -and ``NaN(@var{optional hex-number})''. If @var{endp} is not a null -pointer, a pointer to the first unconverted character will be stored -in the location pointed to by @var{endp}. +floating point number into that number. The floating point number may +also take the form of a hex floating point number (case-insensitively) +like this [+|-]0xH.HHHp[+|-]DDD. It also recognises (case-insensitively) +``Inf'', ``Infinity'', ``NaN'', ``NaN(@var{optional decimal-number})'', +``NaN(@var{optional octal-number})'' and ``NaN(@var{optional hex-number})''. +If @var{endp} is not a null pointer, a pointer to the first unconverted +character will be stored in the location pointed to by @var{endp}. There is also a standardised version of this function: @code{strtold} (@pxref{strtold}). @subheading Return Value -The value representedby @var{s}. +The value 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 @@ -63,6 +63,7 @@ char buf[] = "123ret"; char buf2[] = "0x123ret"; char buf3[] = "NAN(123)"; char buf4[] = "NAN(0x123)"; +char buf5[] = "0x1234567.89ABCDefP+1234 foobar"; char *bp; long double x, x2, x3, x4; @@ -70,6 +71,7 @@ x = _strtold(buf, &bp); x2 = _strtold(buf2, &bp); x3 = _strtold(buf3, &bp); x4 = _strtold(buf4, &bp); +x5 = _strtold(buf5, &bp); @end example @node strtold, string @@ -79,19 +81,19 @@ x4 = _strtold(buf4, &bp); @example #include -long double strtold(const char *s, char **endp); +long double strtold(const char *@var{s}, char **@var{endp}); @end example @subheading Description This function converts as many characters of @var{s} that look like a -floating point number into that number. It also recognises -(case-insensitively) ``Inf'', ``Infinity'', ``NaN'', -``NaN(@var{optional decimal-number}), -``NaN(@var{optional octal-number}) -and ``NaN(@var{optional hex-number})''. If @var{endp} is not a null -pointer, a pointer to the first unconverted character will be stored -in the location pointed to by @var{endp}. +floating point number into that number. The floating point number may +also take the form of a hex floating point number (case-insensitively) +like this [+|-]0xH.HHHp[+|-]DDD. It also recognises (case-insensitively) +``Inf'', ``Infinity'', ``NaN'', ``NaN(@var{optional decimal-number})'', +``NaN(@var{optional octal-number})'' and ``NaN(@var{optional hex-number})''. +If @var{endp} is not a null pointer, a pointer to the first unconverted +character will be stored in the location pointed to by @var{endp}. @subheading Return Value @@ -137,6 +139,7 @@ char buf[] = "123ret"; char buf2[] = "0x123ret"; char buf3[] = "NAN(123)"; char buf4[] = "NAN(0x123)"; +char buf5[] = "0x1234567.89abcdefP+1234 foobar"; char *bp; long double x, x2, x3, x4; @@ -144,4 +147,5 @@ x = strtold(buf, &bp); x2 = strtold(buf2, &bp); x3 = strtold(buf3, &bp); x4 = strtold(buf4, &bp); +x5 = strtold(buf5, &bp); @end example diff -aprNU3 djgpp.orig/src/libc/c99/stdlib/strtof.c djgpp/src/libc/c99/stdlib/strtof.c --- djgpp.orig/src/libc/c99/stdlib/strtof.c 2008-04-08 18:50:30 +0000 +++ djgpp/src/libc/c99/stdlib/strtof.c 2008-04-10 21:25:22 +0000 @@ -1,3 +1,4 @@ +/* Copyright (C) 2008 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ @@ -16,6 +17,18 @@ #include #include +#define HEX_DIGIT_SIZE (4) +#define FLOAT_BIAS (0x7FU) +#define MAX_BIN_EXPONENT (127) /* Max. and min. binary exponent (inclusive) as */ +#define MIN_BIN_EXPONENT (-126) /* defined in Intel manual (253665.pdf, Table 4.2). */ +#define IS_DEC_DIGIT(x) (((x) >= '0') && ((x) <= '9')) +#define IS_HEX_DIGIT(x) ((((x) >= 'A') && ((x) <= 'F')) || \ + (((x) >= 'a') && ((x) <= 'f')) || \ + IS_DEC_DIGIT(x)) +#define IS_EXPONENT(x) (((x[0]) == 'P' || (x[0]) == 'p') && \ + (x[1] == '+' || x[1] == '-' || IS_DEC_DIGIT(x[1]))) + + float strtof(const char *s, char **sret) { @@ -113,8 +126,170 @@ strtof(const char *s, char **sret) return (t.f); } + /* Handle 0xH.HHH[p|P][+|-]DDD. */ + if ( ! strnicmp( "0x", s, 2 ) ) + { + int digits, integer_digits; + int bin_exponent; + unsigned long int mantissa; + _float_union_t f_value; + + + /* + * Mantissa. + * 6 hex digits fit into the mantissa + * including the implicit integer bit. + */ + bin_exponent = 0; + integer_digits = 0; + mantissa = 0x00U; + s += 2; + while (integer_digits < 8 && IS_HEX_DIGIT(*s)) + { + flags = 1; + mantissa <<= HEX_DIGIT_SIZE; + mantissa += IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + if (mantissa) /* Discarts leading zeros. */ + integer_digits++; /* Counts hex digits. 16**integer_digits. */ + s++; + } + if (integer_digits) + { + /* + * Compute the binary exponent for a normalized mantissa by + * shifting the decimal point inside the most significant hex digit. + */ + unsigned long long bit = 0x01ULL; + + digits = 0; + if (IS_HEX_DIGIT(*s)) + { + char *t = unconst(s, char *); + + for (; IS_HEX_DIGIT(*t); t++) + digits++; /* Counts hex digits. */ + } + bin_exponent = integer_digits * HEX_DIGIT_SIZE - 1; /* 2**bin_exponent. */ + for (bit <<= bin_exponent; !(mantissa & bit); bin_exponent--) + bit >>= 1; + bin_exponent += digits * HEX_DIGIT_SIZE; + } + + if (*s == decimal) + { + int fraction_zeros = 0; + + s++; + digits = integer_digits; + while ((digits - fraction_zeros) < 16 && IS_HEX_DIGIT(*s)) + { + flags = 1; + digits++; /* Counts hex digits. */ + mantissa <<= HEX_DIGIT_SIZE; + mantissa += IS_DEC_DIGIT(*s) ? *s - '0' : + ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10; + if (mantissa == 0) + fraction_zeros++; /* Counts hex zeros. 16**(-fraction_zeros + 1). */ + s++; + } + if (!integer_digits && mantissa) + { + /* + * Compute the binary exponent for a normalized mantissa by + * shifting the decimal point inside the most significant hex digit. + */ + unsigned long long bit = 0x01ULL; + + bin_exponent = -fraction_zeros * HEX_DIGIT_SIZE; /* 2**bin_exponent. */ + for (bit <<= (digits * HEX_DIGIT_SIZE + bin_exponent); !(mantissa & bit); bin_exponent--) + bit >>= 1; + } + } + + if (!flags) + { + if (sret) + *sret = unconst(s, char *); + errno = EINVAL; /* No valid mantissa, no convertion could be performed. */ + return 0.0; + } + + if (mantissa) + { + /* + * Normalize mantissa. + */ + while (!(mantissa & 0x80000000ULL)) + mantissa <<= 1; /* Shift a binary 1 into the integer part of the mantissa. */ + mantissa >>= (31 - 23); + /* At this point the mantissa is normalized and the exponent has been adjusted accordingly. */ + } + + + /* + * After discarting all hex digits left, + * if the next character is P or p + * continue with the extracting of the + * exponent, else any other character + * that have appeared terminates the number. + */ + while (IS_HEX_DIGIT(*s)) + s++; + + /* + * Exponent. + */ + if (IS_EXPONENT(s)) + { + int exponent = 0.0; + s++; + if (*s == '+') + s++; + else if (*s == '-') + { + esign = -1; + s++; + } + + while ((esign * exponent + bin_exponent) < (MAX_BIN_EXPONENT + 1) && IS_DEC_DIGIT(*s)) + { + exponent *= 10; + exponent += *s - '0'; + s++; + } + bin_exponent += esign * exponent; /* 2**bin_exponent. */ + while (IS_DEC_DIGIT(*s)) + s++; /* Discart rest of exponent. */ + } + + + if (sret) + *sret = unconst(s, char *); + if (mantissa) + { + if (bin_exponent > MAX_BIN_EXPONENT) + { + errno = ERANGE; + return sign * HUGE_VALF; + } + else if(bin_exponent < MIN_BIN_EXPONENT) + { + errno = ERANGE; + return 0.0; + } + f_value.ft.sign = (sign == 1) ? 0 : 1; + f_value.ft.exponent = 0x07FFU & (bin_exponent + FLOAT_BIAS); + f_value.ft.mantissa = 0x007FFFFFU & mantissa; + } + else + f_value.f = sign * 0.0; + + return f_value.f; + } + /* Handle ordinary numbers. */ - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { flags |= 1; r *= 10.0; @@ -151,7 +326,7 @@ strtof(const char *s, char **sret) s++; esign = -1; } - while ((*s >= '0') && (*s <= '9')) + while (IS_DEC_DIGIT(*s)) { e *= 10; e += *s - '0'; diff -aprNU3 djgpp.orig/src/libc/c99/stdlib/strtof.txh djgpp/src/libc/c99/stdlib/strtof.txh --- djgpp.orig/src/libc/c99/stdlib/strtof.txh 2003-10-25 11:16:20 +0000 +++ djgpp/src/libc/c99/stdlib/strtof.txh 2008-04-10 22:31:50 +0000 @@ -5,19 +5,19 @@ @example #include -float strtof(const char *s, char **endp); +float strtof(const char *@var{s}, char **@var{endp}); @end example @subheading Description -This function converts as many characters of @var{s} as look like a -floating point number into that number. It also recognises -(case-insensitively) ``Inf'', ``Infinity'', ``NaN'', -``NaN(@var{optional decimal-number})'', -``NaN(@var{optional octal-number})'' -and ``NaN(@var{optional hex-number})''. If @var{endp} is not a null -pointer, a pointer to the first unconverted character will be stored -in the location pointed to by @var{endp}. +This function converts as many characters of @var{s} that look like a +floating point number into that number. The floating point number may +also take the form of a hex floating point number (case-insensitively) +like this [+|-]0xH.HHHp[+|-]DDD. It also recognises (case-insensitively) +``Inf'', ``Infinity'', ``NaN'', ``NaN(@var{optional decimal-number})'', +``NaN(@var{optional octal-number})'' and ``NaN(@var{optional hex-number})''. +If @var{endp} is not a null pointer, a pointer to the first unconverted +character will be stored in the location pointed to by @var{endp}. @subheading Return Value @@ -65,6 +65,7 @@ char buf[] = "123ret"; char buf2[] = "0x123ret"; char buf3[] = "NAN(123)"; char buf4[] = "NAN(0x123)"; +char buf5[] = "0x1234567.89ABCDefP+12 foobar"; char *bp; float x, x2, x3, x4; @@ -72,4 +73,5 @@ x = strtof(buf, &bp); x2 = strtof(buf2, &bp); x3 = strtof(buf3, &bp); x4 = strtof(buf4, &bp); +x5 = strtod(buf5, &bp); @end example diff -aprNU3 djgpp.orig/tests/libc/ansi/stdlib/makefile djgpp/tests/libc/ansi/stdlib/makefile --- djgpp.orig/tests/libc/ansi/stdlib/makefile 2005-05-11 19:59:36 +0000 +++ djgpp/tests/libc/ansi/stdlib/makefile 2008-04-10 22:41:04 +0000 @@ -4,6 +4,7 @@ SRC += env.c SRC += getenv.c SRC += mbstowcs.c SRC += shell.c +SRC += strtold.c SRC += strtod.c SRC += system.c SRC += system2.c diff -aprNU3 djgpp.orig/tests/libc/ansi/stdlib/strtod.c djgpp/tests/libc/ansi/stdlib/strtod.c --- djgpp.orig/tests/libc/ansi/stdlib/strtod.c 2003-05-10 17:16:28 +0000 +++ djgpp/tests/libc/ansi/stdlib/strtod.c 2008-04-10 22:40:42 +0000 @@ -19,6 +19,48 @@ static const char *testnum[] = { "nan(0)", /* nan */ "Nan(1)", /* nan */ "-NaN(0xfffff)", /* nan */ + "-NaN(0xfffff.ffffffp+123456)", /* nan */ + 0 +}; + +static const char *test_string[] = { + /* Integers. */ + "0x1 ###", + "0x2 ###", + "0xF ###", + "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ###", + /* Zeros. */ + "0x0 ###", + "0x0. ###", + "0x.0 ###", + "0x000.000 ###", + "0x0.0P-00###", + "0x0.0P+00###", + "0x0.P-00###", + "0x.0P-00###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+123###", + "0x0.0P+9999999999999999###", + /* Floating points. */ + "0x1. ###", + "0x1.P00###", + "0x1P+0###", + "0x.1P-0###", + "0x12345678.9ABCDEF1p+345 ###", + "0xF.EDcba ###", + "0x123456789abcdef.fedcba987654321 ###", + /* Overflow. */ + "0x1.0P+9999999999999999###", + "0x7.8P+123456###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+987654321###", + /* Underflow. */ + "0x1.0P-9999999999999999###", + "0x7.8P-123456789###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P-987654321###", + /* Errors. */ + "0x 123 ###", + "0x123. 123 ###", + "0x123.123 p123 ###", + "0x123.123p 123 ###", 0 }; @@ -38,5 +80,22 @@ int main (void) } } + for (i = 0; test_string[i]; i++) + { + errno = 0; + result = strtod(test_string[i], &endp); + switch (errno) + { + case 0: + printf("test string: <%s>\tendp: <%s>\tvalue: %e\n", test_string[i], endp, result); + break; + case ERANGE: + printf("test string: <%s>\tendp: <%s>\tvalue: %e ERANGE\n", test_string[i], endp, result); + break; + case EINVAL: + printf("test string: <%s>\tendp: <%s>\tvalue: %e EINVAL\n", test_string[i], endp, result); + break; + } + } return 0; } diff -aprNU3 djgpp.orig/tests/libc/ansi/stdlib/strtold.c djgpp/tests/libc/ansi/stdlib/strtold.c --- djgpp.orig/tests/libc/ansi/stdlib/strtold.c 1970-01-01 00:00:00 +0000 +++ djgpp/tests/libc/ansi/stdlib/strtold.c 2008-04-10 22:40:42 +0000 @@ -0,0 +1,102 @@ +#include +#include +#include + +static const char *testnum[] = { + "0e200", + "1e2000", + "0e2000000000", /* suggested by Morten Welinder */ + "1e6000000000", /* overflow */ + "1e5000", /* ditto */ + "1e-5000", /* underflow */ + "InF", /* infinity */ + "-inf", /* infinity */ + "infinity", /* infinity */ + "-inFinitY", /* infinity */ + "nAn", /* nan */ + "-nan", /* nan */ + "Nan()", /* nan */ + "nan(0)", /* nan */ + "Nan(1)", /* nan */ + "-NaN(0xffFFFFFfff)", /* nan */ + "-NaN(0xfffff.ffffffp+123456)", /* nan */ + 0 +}; + +static const char *test_string[] = { + /* Integers. */ + "0x1 ###", + "0x2 ###", + "0xF ###", + "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ###", + /* Zeros. */ + "0x0 ###", + "0x0. ###", + "0x.0 ###", + "0x000.000 ###", + "0x0.0P-00###", + "0x0.0P+00###", + "0x0.P-00###", + "0x.0P-00###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+123###", + "0x0.0P+9999999999999999###", + /* Floating points. */ + "0x1. ###", + "0x1.P00###", + "0x1P+0###", + "0x.1P-0###", + "0x12345678.9ABCDEF1p+12345 ###", + "0xF.EDcba ###", + "0x123456789abcdef.fedcba987654321 ###", + /* Overflow. */ + "0x1.0P+9999999999999999###", + "0x7.8P+123456789###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+987654321###", + /* Underflow. */ + "0x1.0P-9999999999999999###", + "0x7.8P-123456789###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P-987654321###", + /* Errors. */ + "0x 123 ###", + "0x123. 123 ###", + "0x123.123 p123 ###", + "0x123.123p 123 ###", + 0 +}; + +int main(void) +{ + char *endp; + long double result; + int i; + + for (i = 0; testnum[i]; i++) + { + printf ("%20s -> %-20.15g\n", testnum[i], strtold (testnum[i], (char **)0)); + if (errno) + { + perror ("strtold"); + errno = 0; + } + } + + for (i = 0; test_string[i]; i++) + { + errno = 0; + result = strtold(test_string[i], &endp); + switch (errno) + { + case 0: + printf("test string: <%s>\tendp: <%s>\tvalue: %Le\n", test_string[i], endp, result); + break; + case ERANGE: + printf("test string: <%s>\tendp: <%s>\tvalue: %Le ERANGE\n", test_string[i], endp, result); + break; + case EINVAL: + printf("test string: <%s>\tendp: <%s>\tvalue: %Le EINVAL\n", test_string[i], endp, result); + break; + } + } + return 0; +} + diff -aprNU3 djgpp.orig/tests/libc/c99/stdlib/makefile djgpp/tests/libc/c99/stdlib/makefile --- djgpp.orig/tests/libc/c99/stdlib/makefile 2003-01-08 20:18:48 +0000 +++ djgpp/tests/libc/c99/stdlib/makefile 2008-04-10 22:43:58 +0000 @@ -2,5 +2,6 @@ TOP=../.. SRC += t-_exit.c SRC += t-strtof.c +SRC += strtof.c include $(TOP)/../makefile.inc diff -aprNU3 djgpp.orig/tests/libc/c99/stdlib/strtof.c djgpp/tests/libc/c99/stdlib/strtof.c --- djgpp.orig/tests/libc/c99/stdlib/strtof.c 1970-01-01 00:00:00 +0000 +++ djgpp/tests/libc/c99/stdlib/strtof.c 2008-04-10 22:43:44 +0000 @@ -0,0 +1,101 @@ +#include +#include +#include + +static const char *testnum[] = { + "0e20", + "1e200", + "0e2000000000", /* suggested by Morten Welinder */ + "1e6000000000", /* overflow */ + "1e400", /* ditto */ + "1e-400", /* underflow */ + "InF", /* infinity */ + "-inf", /* infinity */ + "infinity", /* infinity */ + "-inFinitY", /* infinity */ + "nAn", /* nan */ + "-nan", /* nan */ + "Nan()", /* nan */ + "nan(0)", /* nan */ + "Nan(1)", /* nan */ + "-NaN(0xfffff)", /* nan */ + "-NaN(0xfffff.ffffffp+123456)", /* nan */ + 0 +}; + +static const char *test_string[] = { + /* Integers. */ + "0x1 ###", + "0x2 ###", + "0xF ###", + "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ###", + /* Zeros. */ + "0x0 ###", + "0x0. ###", + "0x.0 ###", + "0x000.000 ###", + "0x0.0P-00###", + "0x0.0P+00###", + "0x0.P-00###", + "0x.0P-00###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+23###", + "0x0.0P+9999999999999999###", + /* Floating points. */ + "0x1. ###", + "0x1.P00###", + "0x1P+0###", + "0x.1P-0###", + "0x12345678.9ABCDEF1p+34 ###", + "0xF.EDcba ###", + "0x123456789abcdef.fedcba987654321 ###", + /* Overflow. */ + "0x1.0P+9999999999999999###", + "0x7.8P+1234###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P+987654321###", + /* Underflow. */ + "0x1.0P-9999999999999999###", + "0x7.8P-123456###", + "0X00000.0000000000000000000000000000000000000000000000000000000000FED0123P-987654###", + /* Errors. */ + "0x 123 ###", + "0x123. 123 ###", + "0x123.123 p123 ###", + "0x123.123p 123 ###", + 0 +}; + +int main (void) +{ + int i; + + errno = 0; + + for (i = 0; testnum[i]; i++) + { + printf ("%20s -> %-20.15g\n", testnum[i], strtof (testnum[i], (char **)0)); + if (errno) + { + perror ("strtod"); + errno = 0; + } + } + + for (i = 0; test_string[i]; i++) + { + errno = 0; + result = strtof(test_string[i], &endp); + switch (errno) + { + case 0: + printf("test string: <%s>\tendp: <%s>\tvalue: %e\n", test_string[i], endp, result); + break; + case ERANGE: + printf("test string: <%s>\tendp: <%s>\tvalue: %e ERANGE\n", test_string[i], endp, result); + break; + case EINVAL: + printf("test string: <%s>\tendp: <%s>\tvalue: %e EINVAL\n", test_string[i], endp, result); + break; + } + } + return 0; +}