[Python-Dev] LC_NUMERIC and C libraries

Gustavo J A M Carneiro gjc@inescporto.pt
19 Jul 2003 14:08:28 +0100


--=-UjU31ROv+4O9JKpq7ZBY
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

  Here's the patch. Also 2 new source files:
	- pystrtod.h goes to Include/
	- pystrtod.c goes to Python/

  The next step is to ask glib authors for permission to relicense the
code.  Also, the locale module should be changed, to allow LC_NUMERIC to
be changed, etc.

  Regards.

-- 
Gustavo J. A. M. Carneiro
<gjc@inescporto.pt> <gustavo@users.sourceforge.net>

--=-UjU31ROv+4O9JKpq7ZBY
Content-Disposition: attachment; filename=numeric.diff
Content-Type: text/x-patch; name=numeric.diff; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

Only in Python-2.3b2-gjc/Include: pystrtod.h
diff -rup Python-2.3b2/Include/Python.h Python-2.3b2-gjc/Include/Python.h
--- Python-2.3b2/Include/Python.h	2002-08-12 08:21:56.000000000 +0100
+++ Python-2.3b2-gjc/Include/Python.h	2003-07-17 00:03:47.000000000 +0100
@@ -113,6 +113,8 @@
 
 #include "abstract.h"
 
+#include "pystrtod.h"
+
 /* _Py_Mangle is defined in compile.c */
 PyAPI_FUNC(int) _Py_Mangle(char *p, char *name, \
 				 char *buffer, size_t maxlen);
diff -rup Python-2.3b2/Makefile.pre.in Python-2.3b2-gjc/Makefile.pre.in
--- Python-2.3b2/Makefile.pre.in	2003-06-21 14:26:28.000000000 +0100
+++ Python-2.3b2-gjc/Makefile.pre.in	2003-07-16 23:54:30.000000000 +0100
@@ -244,6 +244,7 @@ PYTHON_OBJS=	\
 		Python/sysmodule.o \
 		Python/traceback.o \
 		Python/getopt.o \
+		Python/pystrtod.o \
 		Python/$(DYNLOADFILE) \
 		$(MACHDEP_OBJS) \
 		$(THREADOBJ)
diff -rup Python-2.3b2/Modules/cPickle.c Python-2.3b2-gjc/Modules/cPickle.c
--- Python-2.3b2/Modules/cPickle.c	2003-06-16 21:19:49.000000000 +0100
+++ Python-2.3b2-gjc/Modules/cPickle.c	2003-07-19 12:08:17.000000000 +0100
@@ -3314,7 +3314,7 @@ load_float(Unpicklerobject *self)
 	if (!( s=pystrndup(s,len)))  return -1;
 
 	errno = 0;
-	d = strtod(s, &endptr);
+	d = PyOS_strtod(s, &endptr);
 
 	if (errno || (endptr[0] != '\n') || (endptr[1] != '\0')) {
 		PyErr_SetString(PyExc_ValueError,
diff -rup Python-2.3b2/Modules/stropmodule.c Python-2.3b2-gjc/Modules/stropmodule.c
--- Python-2.3b2/Modules/stropmodule.c	2002-08-02 03:27:13.000000000 +0100
+++ Python-2.3b2-gjc/Modules/stropmodule.c	2003-07-19 12:08:17.000000000 +0100
@@ -838,7 +838,6 @@ PyDoc_STRVAR(atof__doc__,
 static PyObject *
 strop_atof(PyObject *self, PyObject *args)
 {
-	extern double strtod(const char *, char **);
 	char *s, *end;
 	double x;
 	char buffer[256]; /* For errors */
@@ -854,7 +853,7 @@ strop_atof(PyObject *self, PyObject *arg
 	}
 	errno = 0;
 	PyFPE_START_PROTECT("strop_atof", return 0)
-	x = strtod(s, &end);
+	x = PyOS_strtod(s, &end);
 	PyFPE_END_PROTECT(x)
 	while (*end && isspace(Py_CHARMASK(*end)))
 		end++;
diff -rup Python-2.3b2/Objects/complexobject.c Python-2.3b2-gjc/Objects/complexobject.c
--- Python-2.3b2/Objects/complexobject.c	2003-06-17 21:22:24.000000000 +0100
+++ Python-2.3b2-gjc/Objects/complexobject.c	2003-07-19 12:56:59.000000000 +0100
@@ -272,13 +272,19 @@ complex_dealloc(PyObject *op)
 static void
 complex_to_buf(char *buf, int bufsz, PyComplexObject *v, int precision)
 {
-	if (v->cval.real == 0.)
-		PyOS_snprintf(buf, bufsz, "%.*gj",
-			      precision, v->cval.imag);
-	else
-		PyOS_snprintf(buf, bufsz, "(%.*g%+.*gj)",
-			      precision, v->cval.real,
-			      precision, v->cval.imag);
+	char format[32];
+	if (v->cval.real == 0.) {
+		PyOS_snprintf(format, 32, "%%.%ig", precision);
+		PyOS_formatd(buf, bufsz, format, v->cval.imag);
+		strncat(buf, "j", bufsz);
+	} else {
+		char re[64], im[64];
+		
+		PyOS_snprintf(format, 32, "%%.%ig", precision);
+		PyOS_formatd(re, 64, format, v->cval.real);
+		PyOS_formatd(im, 64, format, v->cval.imag);
+		PyOS_snprintf(buf, bufsz, "(%s+%sj)", re, im);
+	}
 }
 
 static int
@@ -662,7 +668,6 @@ static PyMemberDef complex_members[] = {
 static PyObject *
 complex_subtype_from_string(PyTypeObject *type, PyObject *v)
 {
-	extern double strtod(const char *, char **);
 	const char *s, *start;
 	char *end;
 	double x=0.0, y=0.0, z;
@@ -774,7 +779,7 @@ complex_subtype_from_string(PyTypeObject
 			}
 			errno = 0;
 			PyFPE_START_PROTECT("strtod", return 0)
-				z = strtod(s, &end) ;
+				z = PyOS_strtod(s, &end) ;
 			PyFPE_END_PROTECT(z)
 				if (errno != 0) {
 					PyOS_snprintf(buffer, sizeof(buffer),
diff -rup Python-2.3b2/Objects/floatobject.c Python-2.3b2-gjc/Objects/floatobject.c
--- Python-2.3b2/Objects/floatobject.c	2003-06-28 21:04:24.000000000 +0100
+++ Python-2.3b2-gjc/Objects/floatobject.c	2003-07-19 12:54:30.000000000 +0100
@@ -142,7 +142,7 @@ PyFloat_FromString(PyObject *v, char **p
 	 * key off errno.
          */
 	PyFPE_START_PROTECT("strtod", return NULL)
-	x = strtod(s, (char **)&end);
+	x = PyOS_strtod(s, (char **)&end);
 	PyFPE_END_PROTECT(x)
 	errno = 0;
 	/* Believe it or not, Solaris 2.6 can move end *beyond* the null
@@ -174,7 +174,7 @@ PyFloat_FromString(PyObject *v, char **p
 		/* See above -- may have been strtod being anal
 		   about denorms. */
 		PyFPE_START_PROTECT("atof", return NULL)
-		x = atof(s);
+		x = PyOS_atof(s);
 		PyFPE_END_PROTECT(x)
 		errno = 0;    /* whether atof ever set errno is undefined */
 	}
@@ -233,6 +233,7 @@ static void
 format_float(char *buf, size_t buflen, PyFloatObject *v, int precision)
 {
 	register char *cp;
+	char format[32];
 	/* Subroutine for float_repr and float_print.
 	   We want float numbers to be recognizable as such,
 	   i.e., they should contain a decimal point or an exponent.
@@ -240,7 +241,8 @@ format_float(char *buf, size_t buflen, P
 	   in such cases, we append ".0" to the string. */
 
 	assert(PyFloat_Check(v));
-	PyOS_snprintf(buf, buflen, "%.*g", precision, v->ob_fval);
+	PyOS_snprintf(format, 32, "%%.%ig", precision);
+	PyOS_formatd(buf, buflen, format, v->ob_fval);
 	cp = buf;
 	if (*cp == '-')
 		cp++;
diff -rup Python-2.3b2/Python/compile.c Python-2.3b2-gjc/Python/compile.c
--- Python-2.3b2/Python/compile.c	2003-06-20 17:13:17.000000000 +0100
+++ Python-2.3b2-gjc/Python/compile.c	2003-07-19 12:06:56.000000000 +0100
@@ -1286,7 +1286,7 @@ parsenumber(struct compiling *c, char *s
 		Py_complex z;
 		z.real = 0.;
 		PyFPE_START_PROTECT("atof", return 0)
-		z.imag = atof(s);
+		z.imag = PyOS_atof(s);
 		PyFPE_END_PROTECT(z)
 		return PyComplex_FromCComplex(z);
 	}
@@ -1294,7 +1294,7 @@ parsenumber(struct compiling *c, char *s
 #endif
 	{
 		PyFPE_START_PROTECT("atof", return 0)
-		dx = atof(s);
+		dx = PyOS_atof(s);
 		PyFPE_END_PROTECT(dx)
 		return PyFloat_FromDouble(dx);
 	}
diff -rup Python-2.3b2/Python/marshal.c Python-2.3b2-gjc/Python/marshal.c
--- Python-2.3b2/Python/marshal.c	2002-07-30 12:44:44.000000000 +0100
+++ Python-2.3b2-gjc/Python/marshal.c	2003-07-19 12:11:08.000000000 +0100
@@ -447,7 +447,7 @@ r_object(RFILE *p)
 			}
 			buf[n] = '\0';
 			PyFPE_START_PROTECT("atof", return 0)
-			dx = atof(buf);
+			dx = PyOS_atof(buf);
 			PyFPE_END_PROTECT(dx)
 			return PyFloat_FromDouble(dx);
 		}
@@ -465,7 +465,7 @@ r_object(RFILE *p)
 			}
 			buf[n] = '\0';
 			PyFPE_START_PROTECT("atof", return 0)
-			c.real = atof(buf);
+			c.real = PyOS_atof(buf);
 			PyFPE_END_PROTECT(c)
 			n = r_byte(p);
 			if (r_string(buf, (int)n, p) != n) {
@@ -475,7 +475,7 @@ r_object(RFILE *p)
 			}
 			buf[n] = '\0';
 			PyFPE_START_PROTECT("atof", return 0)
-			c.imag = atof(buf);
+			c.imag = PyOS_atof(buf);
 			PyFPE_END_PROTECT(c)
 			return PyComplex_FromCComplex(c);
 		}
Only in Python-2.3b2-gjc/Python: pystrtod.c

--=-UjU31ROv+4O9JKpq7ZBY
Content-Disposition: attachment; filename=pystrtod.c
Content-Type: text/x-c; name=pystrtod.c; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

/* -*- Mode: C; c-file-style: "python" -*- */
#include "Python.h"
#include <stdlib.h>
#include <locale.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

//#define __PYSTRTOD_DEBUG

/* Note: glib functions are based on gstrfuncs.c, version 1.94.2.3 */

/* ------------------------------------------ */
  /* <glib-stuff> */
    /* glib uses gnu-style indenting/spacing */

    /* for compatibility */
#ifdef NDEBUG
# define g_return_val_if_fail(condition, value)
# define g_return_if_fail(condition)
#else
# define g_return_val_if_fail(condition, value)	\
	if (!(condition)) {			\
		/*give warning here*/;		\
		return (value);			\
        }
# define g_return_if_fail(condition, value)	\
	if (!(condition)) {			\
		/*give warning here*/;		\
		return;				\
        }
#endif
#define g_assert(foo) assert(foo)
#define g_malloc(foo) malloc(foo)
#define g_free(foo)   free(foo)
#define _g_snprintf PyOS_snprintf

    /* ... */
typedef char		gchar;
typedef short		gshort;
typedef long   		glong;
typedef int    		gint;
typedef gint   		gboolean;
typedef unsigned char	guchar;
typedef unsigned short	gushort;
typedef unsigned long	gulong;
typedef unsigned int	guint;
typedef float		gfloat;
typedef double		gdouble;
typedef signed char	gint8;
typedef unsigned char	guint8;
typedef signed short	gint16;
typedef unsigned short	guint16;
typedef signed int	gint32;
typedef unsigned int	guint32;

static const guint16 ascii_table_data[256] = {
  0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
  0x004, 0x104, 0x104, 0x004, 0x104, 0x104, 0x004, 0x004,
  0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
  0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
  0x140, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
  0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
  0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
  0x459, 0x459, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
  0x0d0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
  0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
  0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
  0x253, 0x253, 0x253, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
  0x0d0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
  0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
  0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
  0x073, 0x073, 0x073, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x004
  /* the upper 128 are all zeroes */
};

const guint16 * const g_ascii_table = ascii_table_data;

/* Functions like the ones in <ctype.h> that are not affected by locale. */
typedef enum {
  G_ASCII_ALNUM  = 1 << 0,
  G_ASCII_ALPHA  = 1 << 1,
  G_ASCII_CNTRL  = 1 << 2,
  G_ASCII_DIGIT  = 1 << 3,
  G_ASCII_GRAPH  = 1 << 4,
  G_ASCII_LOWER  = 1 << 5,
  G_ASCII_PRINT  = 1 << 6,
  G_ASCII_PUNCT  = 1 << 7,
  G_ASCII_SPACE  = 1 << 8,
  G_ASCII_UPPER  = 1 << 9,
  G_ASCII_XDIGIT = 1 << 10
} GAsciiType;

#define g_ascii_isalnum(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_ALNUM) != 0)

#define g_ascii_isalpha(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_ALPHA) != 0)

#define g_ascii_iscntrl(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_CNTRL) != 0)

#define g_ascii_isdigit(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_DIGIT) != 0)

#define g_ascii_isgraph(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_GRAPH) != 0)

#define g_ascii_islower(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_LOWER) != 0)

#define g_ascii_isprint(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_PRINT) != 0)

#define g_ascii_ispunct(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_PUNCT) != 0)

#define g_ascii_isspace(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)

#define g_ascii_isupper(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_UPPER) != 0)

#define g_ascii_isxdigit(c) \
  ((g_ascii_table[(guchar) (c)] & G_ASCII_XDIGIT) != 0)



/**
 * g_ascii_strtod:
 * @nptr:    the string to convert to a numeric value.
 * @endptr:  if non-%NULL, it returns the character after
 *           the last character used in the conversion.
 * 
 * Converts a string to a #gdouble value.
 * This function behaves like the standard strtod() function
 * does in the C locale. It does this without actually
 * changing the current locale, since that would not be
 * thread-safe.
 *
 * This function is typically used when reading configuration
 * files or other non-user input that should be locale independent.
 * To handle input from the user you should normally use the
 * locale-sensitive system strtod() function.
 *
 * To convert from a #gdouble to a string in a locale-insensitive
 * way, use g_ascii_dtostr().
 *
 * If the correct value would cause overflow, plus or minus %HUGE_VAL
 * is returned (according to the sign of the value), and %ERANGE is
 * stored in %errno. If the correct value would cause underflow,
 * zero is returned and %ERANGE is stored in %errno.
 * 
 * This function resets %errno before calling strtod() so that
 * you can reliably detect overflow and underflow.
 *
 * Return value: the #gdouble value.
 **/
static gdouble
g_ascii_strtod (const gchar *nptr,
		gchar      **endptr)
{
  gchar *fail_pos;
  gdouble val;
  struct lconv *locale_data;
  const char *decimal_point;
  int decimal_point_len;
  const char *p, *decimal_point_pos;
  const char *end = NULL; /* Silence gcc */

  g_return_val_if_fail (nptr != NULL, 0);

  fail_pos = NULL;

  locale_data = localeconv ();
  decimal_point = locale_data->decimal_point;
  decimal_point_len = strlen (decimal_point);

  g_assert (decimal_point_len != 0);
  
  decimal_point_pos = NULL;
  if (decimal_point[0] != '.' ||
      decimal_point[1] != 0)
    {
      p = nptr;
      /* Skip leading space */
      while (g_ascii_isspace (*p))
	p++;
      
      /* Skip leading optional sign */
      if (*p == '+' || *p == '-')
	p++;
      
      if (p[0] == '0' &&
	  (p[1] == 'x' || p[1] == 'X'))
	{
	  p += 2;
	  /* HEX - find the (optional) decimal point */
	  
	  while (g_ascii_isxdigit (*p))
	    p++;
	  
	  if (*p == '.')
	    {
	      decimal_point_pos = p++;
	      
	      while (g_ascii_isxdigit (*p))
		p++;
	      
	      if (*p == 'p' || *p == 'P')
		p++;
	      if (*p == '+' || *p == '-')
		p++;
	      while (g_ascii_isdigit (*p))
		p++;
	      end = p;
	    }
	}
      else
	{
	  while (g_ascii_isdigit (*p))
	    p++;
	  
	  if (*p == '.')
	    {
	      decimal_point_pos = p++;
	      
	      while (g_ascii_isdigit (*p))
		p++;
	      
	      if (*p == 'e' || *p == 'E')
		p++;
	      if (*p == '+' || *p == '-')
		p++;
	      while (g_ascii_isdigit (*p))
		p++;
	      end = p;
	    }
	}
      /* For the other cases, we need not convert the decimal point */
    }

  /* Set errno to zero, so that we can distinguish zero results
     and underflows */
  errno = 0;
  
  if (decimal_point_pos)
    {
      char *copy, *c;

      /* We need to convert the '.' to the locale specific decimal point */
      copy = g_malloc (end - nptr + 1 + decimal_point_len);
      
      c = copy;
      memcpy (c, nptr, decimal_point_pos - nptr);
      c += decimal_point_pos - nptr;
      memcpy (c, decimal_point, decimal_point_len);
      c += decimal_point_len;
      memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
      c += end - (decimal_point_pos + 1);
      *c = 0;

      val = strtod (copy, &fail_pos);

      if (fail_pos)
	{
	  if (fail_pos > decimal_point_pos)
	    fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
	  else
	    fail_pos = (char *)nptr + (fail_pos - copy);
	}
      
      g_free (copy);
	  
    }
  else
    val = strtod (nptr, &fail_pos);

  if (endptr)
    *endptr = fail_pos;
  
  return val;
}


/**
 * g_ascii_formatd:
 * @buffer: A buffer to place the resulting string in
 * @buf_len: The length of the buffer.
 * @format: The printf()-style format to use for the
 *          code to use for converting. 
 * @d: The #gdouble to convert
 *
 * Converts a #gdouble to a string, using the '.' as
 * decimal point. To format the number you pass in
 * a printf()-style format string. Allowed conversion
 * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'. 
 * 
 * If you just want to want to serialize the value into a
 * string, use g_ascii_dtostr().
 *
 * Return value: The pointer to the buffer with the converted string.
 **/
static gchar *
g_ascii_formatd (gchar       *buffer,
		 gint         buf_len,
		 const gchar *format,
		 gdouble      d)
{
  struct lconv *locale_data;
  const char *decimal_point;
  int decimal_point_len;
  gchar *p;
  int rest_len;
  gchar format_char;

  g_return_val_if_fail (buffer != NULL, NULL);
  g_return_val_if_fail (format[0] == '%', NULL);
  g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL);
 
  format_char = format[strlen (format) - 1];
  
  g_return_val_if_fail (format_char == 'e' || format_char == 'E' ||
			format_char == 'f' || format_char == 'F' ||
			format_char == 'g' || format_char == 'G',
			NULL);

  if (format[0] != '%')
    return NULL;

  if (strpbrk (format + 1, "'l%"))
    return NULL;

  if (!(format_char == 'e' || format_char == 'E' ||
	format_char == 'f' || format_char == 'F' ||
	format_char == 'g' || format_char == 'G'))
    return NULL;

      
  _g_snprintf (buffer, buf_len, format, d);

  locale_data = localeconv ();
  decimal_point = locale_data->decimal_point;
  decimal_point_len = strlen (decimal_point);

  g_assert (decimal_point_len != 0);

  if (decimal_point[0] != '.' ||
      decimal_point[1] != 0)
    {
      p = buffer;

      if (*p == '+' || *p == '-')
	p++;

      while (isdigit ((guchar)*p))
	p++;

      if (strncmp (p, decimal_point, decimal_point_len) == 0)
	{
	  *p = '.';
	  p++;
	  if (decimal_point_len > 1) {
	    rest_len = strlen (p + (decimal_point_len-1));
	    memmove (p, p + (decimal_point_len-1),
		     rest_len);
	    p[rest_len] = 0;
	    
	  }
	}
    }
  
  return buffer;
}


  /* </glib-stuff> */
/* ------------------------------------------ */
#ifdef __PYSTRTOD_DEBUG
# include <stdio.h>
#endif
double PyOS_strtod(const char *str, char **ptr)
{
#ifdef __PYSTRTOD_DEBUG
	fprintf(stderr, ">>>!!! calling g_ascii_strtod('%s')\n", str);
#endif
	return g_ascii_strtod(str, ptr);
}

double PyOS_atof(const char *str)
{
#ifdef __PYSTRTOD_DEBUG
	fprintf(stderr, ">>>!!! calling g_ascii_strtod('%s')\n", str);
#endif
	return g_ascii_strtod(str, NULL);
}

void PyOS_formatd(char *buffer, int buf_len, const char *format, double d)
{
#ifdef __PYSTRTOD_DEBUG
	fprintf(stderr, ">>>!!! calling g_ascii_formatd('%s', %f)\n", format, d);
#endif
	g_ascii_formatd(buffer, buf_len, format, d);
#ifdef __PYSTRTOD_DEBUG
	fprintf(stderr, ">>>!!! g_ascii_formatd: \"%s\"\n", buffer);
#endif
}


--=-UjU31ROv+4O9JKpq7ZBY
Content-Disposition: attachment; filename=pystrtod.h
Content-Type: text/x-c-header; name=pystrtod.h; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

#ifndef Py_STRTOD_H
#define Py_STRTOD_H

#ifdef __cplusplus
extern "C" {
#endif


double PyOS_strtod(const char *str, char **ptr);
double PyOS_atof(const char *str);
void   PyOS_formatd(char *buffer, int buf_len,  const char *format, double d);


#ifdef __cplusplus
}
#endif

#endif /* !Py_STRTOD_H */

--=-UjU31ROv+4O9JKpq7ZBY--