[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--