delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2008/04/08/16:33:07

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: V01U2FsdGVkX19ch4w1KbNcK0aKkMg3ZGXqhk5knV31ilIZSEHTYy
HvtPcpX3mRbn4b
From: Juan Manuel Guerrero <juan DOT guerrero AT gmx DOT de>
To: djgpp-workers AT delorie DOT com
Subject: Hex float string conversion for strto[dfld]
Date: Tue, 8 Apr 2008 22:32:26 +0200
User-Agent: KMail/1.9.5
MIME-Version: 1.0
Message-Id: <200804082232.28045.juan.guerrero@gmx.de>
X-Y-GMX-Trusted: 0
Reply-To: djgpp-workers AT delorie DOT com

While I was trying to port m4-1.4.11 I got a new suprise.
Now they have started to use a version of strtod that according to:
  <http://www.opengroup.org/onlinepubs/000095399/functions/strtod.html>
must be able to convert hex floating strings like this "0x123.abcP-123".
The patch submitted implements this functionality for the strto[dfld]
family of functions.
Suggestions, objections, comments are welcome.

Regards,
Juan M. Guerrero
 


2008-04-08  Juan Manuel Guerrero  <juan DOT guerrero AT gmx DOT de>
	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.



Index: src/libc/ansi/stdlib/strtod.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/strtod.c,v
retrieving revision 1.10
diff -p -U3 -r1.10 strtod.c
--- src/libc/ansi/stdlib/strtod.c	8 Apr 2008 17:55:48 -0000	1.10
+++ src/libc/ansi/stdlib/strtod.c	8 Apr 2008 20:26:51 -0000
@@ -15,6 +15,16 @@
 #include <libc/unconst.h>
 #include <libc/ieee.h>
 
+#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))
+
+
 double
 strtod(const char *s, char **sret)
 {
@@ -113,8 +123,145 @@ strtod(const char *s, char **sret)
     return (t.d);
   }
 
+  /* Handle 0xH.HHH[p|P][+|-]DDD. */
+  if ( ! strnicmp( "0x", s, 2 ) )
+  {
+    int digits, integer_digits;
+    int exponent;
+    unsigned long long int mantissa;
+    _double_union_t d_value;
+
+
+    /*
+     *  Mantissa.
+     *  13 hex digits fit into the fraction part.
+     */
+    digits = integer_digits = 0;
+    mantissa = 0x00ULL;
+    s += 2;
+    while (integer_digits < 13 && IS_HEX_DIGIT(*s))
+    {
+      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 (*s == decimal)
+    {
+      s++;
+      digits = integer_digits;
+      while (digits < 13 && IS_HEX_DIGIT(*s))
+      {
+        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;
+        s++;
+      }
+    }
+
+    if (!digits && !integer_digits)
+    {
+      if (sret)
+        *sret = unconst(s, char *);
+      errno = EINVAL;  /*  No valid mantissa, no convertion could be performed.  */
+      return 0.0;
+    }
+
+    /*
+     *  Normalize mantissa and adjust exponent accordingly.
+     *  A maximum of four left shifts will be done to shift
+     *  the most significant binary 1 from the fraction into
+     *  the integer part of the 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 & 0x0010000000000000ULL); 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);
+        break;
+      case 2:
+        mantissa |= (0x03ULL & bits >> 2);
+        break;
+      case 3:
+        mantissa |= (0x07ULL & bits >> 1);
+        break;
+      case 4:
+        mantissa |= (0x0FULL & bits);
+        break;
+      }
+    }
+
+    /*
+     *  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.
+     */
+    exponent = 0U;
+    if (*s == 'P' || *s == 'p')
+    {
+      s++;
+      if (*s == '+')
+        s++;
+      else if (*s == '-')
+      {
+        esign = -1;
+        s++;
+      }
+
+      while (exponent < (MAX_BIN_EXPONENT + 1) && IS_DEC_DIGIT(*s))
+      {
+        exponent *= 10;
+        exponent += *s - '0';
+        s++;
+      }
+    }
+    exponent = esign * exponent + integer_digits * HEX_DIGIT_SIZE - 1;  /*  2 ** exponent.  */
+
+
+    if (exponent > MAX_BIN_EXPONENT)
+    {
+      errno = ERANGE;
+      return sign * HUGE_VAL;
+    }
+    else if(exponent < MIN_BIN_EXPONENT)
+    {
+      errno = ERANGE;
+      return 0.0;
+    }
+    d_value.dt.sign      = (sign == 1) ? 0 : 1;
+    d_value.dt.exponent  = 0x07FFU & (exponent + DOUBLE_BIAS);
+    d_value.dt.mantissah = 0x000FFFFFUL & (mantissa >> 32);
+    d_value.dt.mantissal = 0xFFFFFFFFUL & mantissa;
+
+    if (sret)
+      *sret = unconst(s, char *);
+
+    return d_value.d;
+  }
+
   /* Handle ordinary numbers. */
-  while ((*s >= '0') && (*s <= '9'))
+  while (IS_DEC_DIGIT(*s))
   {
     flags |= 1;
     r *= 10.0;
@@ -151,7 +298,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';
Index: src/libc/ansi/stdlib/strtod.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/strtod.txh,v
retrieving revision 1.5
diff -p -U3 -r1.5 strtod.txh
--- src/libc/ansi/stdlib/strtod.txh	25 Oct 2003 11:15:59 -0000	1.5
+++ src/libc/ansi/stdlib/strtod.txh	8 Apr 2008 20:26:51 -0000
@@ -5,19 +5,19 @@
 @example
 #include <stdlib.h>
 
-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
 
Index: src/libc/ansi/stdlib/strtold.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/strtold.c,v
retrieving revision 1.10
diff -p -U3 -r1.10 strtold.c
--- src/libc/ansi/stdlib/strtold.c	8 Nov 2003 12:19:40 -0000	1.10
+++ src/libc/ansi/stdlib/strtold.c	8 Apr 2008 20:26:52 -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,21 @@
 #include <locale.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <errno.h>
 #include <math.h>
 #include <string.h>
 #include <libc/unconst.h>
 #include <libc/ieee.h>
 
+#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))
+
 static long double powten[] =
 {
   1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, 1e64L, 1e128L, 1e256L,
@@ -116,8 +127,143 @@ 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 exponent;
+    unsigned long long mantissa;
+    _longdouble_union_t ld_value;
+
+
+    /*
+     *  Mantissa.
+     *  16 hex digits fit into the mantissa
+     *  including the explicit integer bit.
+     */
+    digits = integer_digits = 0;
+    mantissa = 0x00ULL;
+    s += 2;
+    while (integer_digits < 16 && IS_HEX_DIGIT(*s))
+    {
+      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 (*s == decimal)
+    {
+      s++;
+      digits = integer_digits;
+      while (digits < 16 && IS_HEX_DIGIT(*s))
+      {
+        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;
+        s++;
+      }
+    }
+
+    if (!digits && !integer_digits)
+    {
+      if (sret)
+        *sret = unconst(s, char *);
+      errno = EINVAL;  /*  No valid mantissa, no convertion could be performed.  */
+      return 0.0L;
+    }
+
+    /*
+     *  Normalize mantissa and adjust exponent accordingly.
+     *  A maximum of three left shifts will be done to shift
+     *  the most significant binary 1 from the fraction into
+     *  the integer part of the 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);
+        break;
+      case 2:
+        mantissa |= (0x03ULL & bits >> 2);
+        break;
+      case 3:
+        mantissa |= (0x07ULL & bits >> 1);
+        break;
+      }
+    }
+
+    /*
+     *  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.
+     */
+    exponent = 0ULL;
+    if (*s == 'P' || *s == 'p')
+    {
+      s++;
+      if (*s == '+')
+        s++;
+      else if (*s == '-')
+      {
+        esign = -1;
+        s++;
+      }
+
+      while (exponent < (MAX_BIN_EXPONENT + 1) && IS_DEC_DIGIT(*s))
+      {
+        exponent *= 10;
+        exponent += *s - '0';
+        s++;
+      }
+    }
+    exponent = esign * exponent + integer_digits * HEX_DIGIT_SIZE - 1;  /*  2 ** exponent.  */
+
+
+    if (exponent > MAX_BIN_EXPONENT)
+    {
+      errno = ERANGE;
+      return sign * HUGE_VALL;
+    }
+    else if(exponent < MIN_BIN_EXPONENT)
+    {
+      errno = ERANGE;
+      return 0.0L;
+    }
+    ld_value.ldt.sign      = (sign == 1) ? 0 : 1;
+    ld_value.ldt.exponent  = 0x7FFFU & (exponent + LONG_DOUBLE_BIAS);
+    ld_value.ldt.mantissah = 0xFFFFFFFFUL & (mantissa >> 32);
+    ld_value.ldt.mantissal = 0xFFFFFFFFUL & mantissa;
+
+    if (sret)
+      *sret = unconst(s, char *);
+
+    return ld_value.ld;
+  }
+
   /* Handle ordinary numbers. */
-  while ((*s >= '0') && (*s <= '9'))
+  while (IS_DEC_DIGIT(*s))
   {
     flags |= 1;
     r *= 10.0L;
@@ -128,7 +274,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 +287,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 +301,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';
Index: src/libc/ansi/stdlib/strtold.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/strtold.txh,v
retrieving revision 1.6
diff -p -U3 -r1.6 strtold.txh
--- src/libc/ansi/stdlib/strtold.txh	25 Oct 2003 11:15:59 -0000	1.6
+++ src/libc/ansi/stdlib/strtold.txh	8 Apr 2008 20:26:52 -0000
@@ -5,26 +5,26 @@
 @example
 #include <stdlib.h>
 
-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 <stdlib.h>
 
-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
Index: src/libc/c99/stdlib/strtof.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/c99/stdlib/strtof.c,v
retrieving revision 1.8
diff -p -U3 -r1.8 strtof.c
--- src/libc/c99/stdlib/strtof.c	8 Apr 2008 18:50:31 -0000	1.8
+++ src/libc/c99/stdlib/strtof.c	8 Apr 2008 20:26:54 -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,16 @@
 #include <libc/unconst.h>
 #include <libc/ieee.h>
 
+#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))
+
+
 float
 strtof(const char *s, char **sret)
 {
@@ -113,8 +124,142 @@ 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 exponent;
+    unsigned int mantissa;
+    _float_union_t f_value;
+
+
+    /*
+     *  Mantissa.
+     *  6 hex digits fit into the mantissa
+     *  including the implicit integer bit.
+     */
+    digits = integer_digits = 0;
+    mantissa = 0x00U;
+    s += 2;
+    while (integer_digits < 6 && IS_HEX_DIGIT(*s))
+    {
+      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 (*s == decimal)
+    {
+      s++;
+      digits = integer_digits;
+      while (digits < 6 && IS_HEX_DIGIT(*s))
+      {
+        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;
+        s++;
+      }
+    }
+
+    if (!digits && !integer_digits)
+    {
+      if (sret)
+        *sret = unconst(s, char *);
+      errno = EINVAL;  /*  No valid mantissa, no convertion could be performed.  */
+      return 0.0;
+    }
+
+    /*
+     *  Normalize mantissa and adjust exponent accordingly.
+     *  A maximum of three left shifts will be done to shift
+     *  the most significant binary 1 from the fraction into
+     *  the integer part of the 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 & 0x00800000U); digits++)
+      mantissa <<= 1;  /*  Shift a binary 1 into the integer part of the mantissa.  */
+    if (IS_HEX_DIGIT(*s))
+    {
+      unsigned int bits = IS_DEC_DIGIT(*s) ? *s - '0' : 
+                          ((*s >= 'A') && (*s <= 'F')) ? *s - 'A' + 10 : *s - 'a' + 10;
+
+      switch (digits)
+      {
+      case 1:
+        mantissa |= (0x01U & bits >> 3);
+        break;
+      case 2:
+        mantissa |= (0x03U & bits >> 2);
+        break;
+      case 3:
+        mantissa |= (0x07U & bits >> 1);
+        break;
+      }
+    }
+
+    /*
+     *  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.
+     */
+    exponent = 0U;
+    if (*s == 'P' || *s == 'p')
+    {
+      s++;
+      if (*s == '+')
+        s++;
+      else if (*s == '-')
+      {
+        esign = -1;
+        s++;
+      }
+
+      while (exponent < (MAX_BIN_EXPONENT + 1) && IS_DEC_DIGIT(*s))
+      {
+        exponent *= 10;
+        exponent += *s - '0';
+        s++;
+      }
+    }
+    exponent = esign * exponent + integer_digits * HEX_DIGIT_SIZE - 1;  /*  2 ** exponent.  */
+
+
+    if (exponent > MAX_BIN_EXPONENT)
+    {
+      errno = ERANGE;
+      return sign * HUGE_VALF;
+    }
+    else if(exponent < MIN_BIN_EXPONENT)
+    {
+      errno = ERANGE;
+      return 0.0;
+    }
+    f_value.ft.sign     = (sign == 1) ? 0 : 1;
+    f_value.ft.exponent = 0x07FFU & (exponent + FLOAT_BIAS);
+    f_value.ft.mantissa = 0x007FFFFFU & mantissa;
+
+    if (sret)
+      *sret = unconst(s, char *);
+
+    return f_value.f;
+  }
+
   /* Handle ordinary numbers. */
-  while ((*s >= '0') && (*s <= '9'))
+  while (IS_DEC_DIGIT(*s))
   {
     flags |= 1;
     r *= 10.0;
@@ -151,7 +296,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';
Index: src/libc/c99/stdlib/strtof.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/c99/stdlib/strtof.txh,v
retrieving revision 1.6
diff -p -U3 -r1.6 strtof.txh
--- src/libc/c99/stdlib/strtof.txh	25 Oct 2003 11:16:20 -0000	1.6
+++ src/libc/c99/stdlib/strtof.txh	8 Apr 2008 20:26:54 -0000
@@ -5,19 +5,19 @@
 @example
 #include <stdlib.h>
 
-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

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019