diff --git a/Doc/c-api/utilities.rst b/Doc/c-api/utilities.rst index 269f23a52a4..eab33a32e09 100644 --- a/Doc/c-api/utilities.rst +++ b/Doc/c-api/utilities.rst @@ -1066,7 +1066,7 @@ The following functions provide locale-independent string to number conversions. .. versionadded:: 2.4 - + .. cfunction:: double PyOS_ascii_atof(const char *nptr) Convert a string to a :ctype:`double` in a locale-independent way. @@ -1075,6 +1075,22 @@ The following functions provide locale-independent string to number conversions. See the Unix man page :manpage:`atof(2)` for details. + +.. cfunction:: char * PyOS_stricmp(char *s1, char *s2) + + Case insensitive comparsion of strings. The functions works almost + identical to :cfunc:`strcmp` except that it ignores the case. + + .. versionadded:: 2.6 + + +.. cfunction:: char * PyOS_strnicmp(char *s1, char *s2, Py_ssize_t size) + + Case insensitive comparsion of strings. The functions works almost + identical to :cfunc:`strncmp` except that it ignores the case. + + .. versionadded:: 2.6 + .. _reflection: diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 1e7119887dd..756d72277fa 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -434,7 +434,8 @@ available. They are listed here in alphabetical order. Convert a string or a number to floating point. If the argument is a string, it must contain a possibly signed decimal or floating point number, possibly - embedded in whitespace. Otherwise, the argument may be a plain or long integer + embedded in whitespace. The argument may also be [+|-]nan or [+|-]inf. + Otherwise, the argument may be a plain or long integer or a floating point number, and a floating point number with the same value (within Python's floating point precision) is returned. If no argument is given, returns ``0.0``. @@ -446,9 +447,10 @@ available. They are listed here in alphabetical order. single: Infinity When passing in a string, values for NaN and Infinity may be returned, depending - on the underlying C library. The specific set of strings accepted which cause - these values to be returned depends entirely on the C library and is known to - vary. + on the underlying C library. Float accepts the strings nan, inf and -inf for + NaN and positive or negative infinity. The case and a leading + are ignored as + well as a leading - is ignored for NaN. Float always represents NaN and infinity + as nan, inf or -inf. The float type is described in :ref:`typesnumeric`. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index f7363c3e886..5b341b81fca 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -298,7 +298,7 @@ numeric operations have a higher priority than comparison operations): +--------------------+---------------------------------+--------+ | ``long(x)`` | *x* converted to long integer | \(2) | +--------------------+---------------------------------+--------+ -| ``float(x)`` | *x* converted to floating point | | +| ``float(x)`` | *x* converted to floating point | \(6) | +--------------------+---------------------------------+--------+ | ``complex(re,im)`` | a complex number with real part | | | | *re*, imaginary part *im*. | | @@ -355,6 +355,13 @@ Notes: Also referred to as integer division. The resultant value is a whole integer, though the result's type is not necessarily int. +(6) + float also accepts the strings "nan" and "inf" with an optional prefix "+" + or "-" for Not a Number (NaN) and positive or negative infinity. + + .. versionadded:: 2.6 + + .. % XXXJH exceptions: overflow (when? what operations?) zerodivision diff --git a/Include/Python.h b/Include/Python.h index f0be28fa74b..56d20abe852 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -129,6 +129,7 @@ #include "eval.h" #include "pystrtod.h" +#include "pystrcmp.h" /* _Py_Mangle is defined in compile.c */ PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); diff --git a/Include/pyport.h b/Include/pyport.h index 0020d7cf6ed..e5cbd5edf4d 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -349,6 +349,17 @@ extern "C" { #define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE) #endif +/* High precision defintion of pi and e (Euler) + * The values are taken from libc6's math.h. + */ +#ifndef Py_MATH_PI +#define Py_MATH_PI 3.1415926535897932384626433832795029L +#endif + +#ifndef Py_MATH_E +#define Py_MATH_E 2.7182818284590452353602874713526625L +#endif + /* Py_IS_NAN(X) * Return 1 if float or double arg is a NaN, else 0. * Caution: @@ -358,8 +369,12 @@ extern "C" { * a platform where it doesn't work. */ #ifndef Py_IS_NAN +#ifdef HAVE_ISNAN +#define Py_IS_NAN(X) isnan(X) +#else #define Py_IS_NAN(X) ((X) != (X)) #endif +#endif /* Py_IS_INFINITY(X) * Return 1 if float or double arg is an infinity, else 0. @@ -370,8 +385,12 @@ extern "C" { * Override in pyconfig.h if you have a better spelling on your platform. */ #ifndef Py_IS_INFINITY +#ifdef HAVE_ISINF +#define Py_IS_INFINITY(X) isinf(X) +#else #define Py_IS_INFINITY(X) ((X) && (X)*0.5 == (X)) #endif +#endif /* Py_IS_FINITE(X) * Return 1 if float or double arg is neither infinite nor NAN, else 0. @@ -379,8 +398,12 @@ extern "C" { * macro for this particular test is useful */ #ifndef Py_IS_FINITE +#ifdef HAVE_ISFINITE +#define Py_IS_FINITE(X) isfinite +#else #define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X)) #endif +#endif /* HUGE_VAL is supposed to expand to a positive double infinity. Python * uses Py_HUGE_VAL instead because some platforms are broken in this @@ -393,6 +416,15 @@ extern "C" { #define Py_HUGE_VAL HUGE_VAL #endif +/* Py_NAN + * A value that evaluates to a NaN. On IEEE 754 platforms INF*0 or + * INF/INF works. Define Py_NO_NAN in pyconfig.h if your platform + * doesn't support NaNs. + */ +#if !defined(Py_NAN) && !defined(Py_NO_NAN) +#define Py_NAN (Py_HUGE_VAL * 0.) +#endif + /* Py_OVERFLOWED(X) * Return 1 iff a libm function overflowed. Set errno to 0 before calling * a libm function, and invoke this macro after, passing the function diff --git a/Include/pystrcmp.h b/Include/pystrcmp.h new file mode 100644 index 00000000000..edb12397e3c --- /dev/null +++ b/Include/pystrcmp.h @@ -0,0 +1,23 @@ +#ifndef Py_STRCMP_H +#define Py_STRCMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(int) PyOS_mystrnicmp(const char *, const char *, Py_ssize_t); +PyAPI_FUNC(int) PyOS_mystricmp(const char *, const char *); + +#ifdef MS_WINDOWS +#define PyOS_strnicmp strnicmp +#define PyOS_stricmp stricmp +#else +#define PyOS_strnicmp PyOS_mystrnicmp +#define PyOS_stricmp PyOS_mystricmp +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_STRCMP_H */ diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index ed5109fb125..11f169054a9 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -3,6 +3,12 @@ import unittest, struct import os from test import test_support +def isinf(x): + return x * 0.5 == x + +def isnan(x): + return x != x + class FormatFunctionsTestCase(unittest.TestCase): def setUp(self): @@ -128,13 +134,78 @@ class ReprTestCase(unittest.TestCase): self.assertEqual(v, eval(repr(v))) floats_file.close() +# Beginning with Python 2.6 float has cross platform compatible +# ways to create and representate inf and nan +class InfNanTest(unittest.TestCase): + def test_inf_from_str(self): + self.assert_(isinf(float("inf"))) + self.assert_(isinf(float("+inf"))) + self.assert_(isinf(float("-inf"))) + + self.assertEqual(repr(float("inf")), "inf") + self.assertEqual(repr(float("+inf")), "inf") + self.assertEqual(repr(float("-inf")), "-inf") + + self.assertEqual(repr(float("INF")), "inf") + self.assertEqual(repr(float("+Inf")), "inf") + self.assertEqual(repr(float("-iNF")), "-inf") + + self.assertEqual(str(float("inf")), "inf") + self.assertEqual(str(float("+inf")), "inf") + self.assertEqual(str(float("-inf")), "-inf") + + self.assertRaises(ValueError, float, "info") + self.assertRaises(ValueError, float, "+info") + self.assertRaises(ValueError, float, "-info") + self.assertRaises(ValueError, float, "in") + self.assertRaises(ValueError, float, "+in") + self.assertRaises(ValueError, float, "-in") + + def test_inf_as_str(self): + self.assertEqual(repr(1e300 * 1e300), "inf") + self.assertEqual(repr(-1e300 * 1e300), "-inf") + + self.assertEqual(str(1e300 * 1e300), "inf") + self.assertEqual(str(-1e300 * 1e300), "-inf") + + def test_nan_from_str(self): + self.assert_(isnan(float("nan"))) + self.assert_(isnan(float("+nan"))) + self.assert_(isnan(float("-nan"))) + + self.assertEqual(repr(float("nan")), "nan") + self.assertEqual(repr(float("+nan")), "nan") + self.assertEqual(repr(float("-nan")), "nan") + + self.assertEqual(repr(float("NAN")), "nan") + self.assertEqual(repr(float("+NAn")), "nan") + self.assertEqual(repr(float("-NaN")), "nan") + + self.assertEqual(str(float("nan")), "nan") + self.assertEqual(str(float("+nan")), "nan") + self.assertEqual(str(float("-nan")), "nan") + + self.assertRaises(ValueError, float, "nana") + self.assertRaises(ValueError, float, "+nana") + self.assertRaises(ValueError, float, "-nana") + self.assertRaises(ValueError, float, "na") + self.assertRaises(ValueError, float, "+na") + self.assertRaises(ValueError, float, "-na") + + def test_nan_as_str(self): + self.assertEqual(repr(1e300 * 1e300 * 0), "nan") + self.assertEqual(repr(-1e300 * 1e300 * 0), "nan") + + self.assertEqual(str(1e300 * 1e300 * 0), "nan") + self.assertEqual(str(-1e300 * 1e300 * 0), "nan") def test_main(): test_support.run_unittest( FormatFunctionsTestCase, UnknownFormatTestCase, IEEEFormatTestCase, - #ReprTestCase + ReprTestCase, + InfNanTest, ) if __name__ == '__main__': diff --git a/Makefile.pre.in b/Makefile.pre.in index 70e5b0e9629..4133d39dc28 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -275,6 +275,7 @@ PYTHON_OBJS= \ Python/sysmodule.o \ Python/traceback.o \ Python/getopt.o \ + Python/pystrcmp.o \ Python/pystrtod.o \ Python/$(DYNLOADFILE) \ $(LIBOBJS) \ @@ -554,6 +555,8 @@ PYTHON_HEADERS= \ Include/pymem.h \ Include/pyport.h \ Include/pystate.h \ + Include/pystrtod.h \ + Include/pystrcmp.h \ Include/pythonrun.h \ Include/rangeobject.h \ Include/setobject.h \ diff --git a/Misc/NEWS b/Misc/NEWS index 40e9e6aac4c..2ae57a4941b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,13 @@ What's New in Python 2.6 alpha 1? Core and builtins ----------------- +- Issue #1635: Platform independent creation and representation of NaN + and INF. float("nan"), float("inf") and float("-inf") now work on every + platform with IEEE 754 semantics. + +- Added case insensitive comparsion methods ``PyOS_stricmp(char*, char*)`` + and ``PyOS_strnicmp(char*, char*, Py_ssize_t)``. + - Compiler now generates simpler and faster code for dictionary literals. The oparg for BUILD_MAP now indicates an estimated dictionary size. There is a new opcode, STORE_MAP, for adding entries to the dictionary. diff --git a/Objects/floatobject.c b/Objects/floatobject.c index a6a29e7f93b..c24b9c5606e 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -128,7 +128,7 @@ still supported but now *officially* useless: if pend is not NULL, PyObject * PyFloat_FromString(PyObject *v, char **pend) { - const char *s, *last, *end; + const char *s, *last, *end, *sp; double x; char buffer[256]; /* for errors */ #ifdef Py_USING_UNICODE @@ -171,6 +171,7 @@ PyFloat_FromString(PyObject *v, char **pend) PyErr_SetString(PyExc_ValueError, "empty string for float()"); return NULL; } + sp = s; /* We don't care about overflow or underflow. If the platform supports * them, infinities and signed zeroes (on underflow) are fine. * However, strtod can return 0 for denormalized numbers, where atof @@ -186,7 +187,26 @@ PyFloat_FromString(PyObject *v, char **pend) byte at the end of the string, when the input is inf(inity). */ if (end > last) end = last; + /* Check for inf and nan. This is done late because it rarely happens. */ if (end == s) { + char *p = (char*)sp; + int sign = 1; + + if (*p == '-') { + sign = -1; + p++; + } + if (*p == '+') { + p++; + } + if (PyOS_strnicmp(p, "inf", 4) == 0) { + return PyFloat_FromDouble(sign * Py_HUGE_VAL); + } +#ifdef Py_NAN + if(PyOS_strnicmp(p, "nan", 4) == 0) { + return PyFloat_FromDouble(Py_NAN); + } +#endif PyOS_snprintf(buffer, sizeof(buffer), "invalid literal for float(): %.200s", s); PyErr_SetString(PyExc_ValueError, buffer); @@ -271,6 +291,8 @@ format_float(char *buf, size_t buflen, PyFloatObject *v, int precision) { register char *cp; char format[32]; + int i; + /* 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. @@ -293,7 +315,33 @@ format_float(char *buf, size_t buflen, PyFloatObject *v, int precision) *cp++ = '.'; *cp++ = '0'; *cp++ = '\0'; + return; } + /* Checking the next three chars should be more than enough to + * detect inf or nan, even on Windows. We check for inf or nan + * at last because they are rare cases. + */ + for (i=0; *cp != '\0' && i<3; cp++, i++) { + if (isdigit(Py_CHARMASK(*cp)) || *cp == '.') + continue; + /* found something that is neither a digit nor point + * it might be a NaN or INF + */ +#ifdef Py_NAN + if (Py_IS_NAN(v->ob_fval)) { + strcpy(buf, "nan"); + } + else +#endif + if (Py_IS_INFINITY(v->ob_fval)) { + cp = buf; + if (*cp == '-') + cp++; + strcpy(cp, "inf"); + } + break; + } + } /* XXX PyFloat_AsStringEx should not be a public API function (for one diff --git a/PC/pyconfig.h b/PC/pyconfig.h index f1f719fa8fd..560818f542e 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -386,6 +386,15 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ /* Fairly standard from here! */ +/* Define to 1 if you have the `copysign' function. */ +/* #define HAVE_COPYSIGN 1*/ + +/* Define to 1 if you have the `isinf' function. */ +#define HAVE_ISINF 1 + +/* Define to 1 if you have the `isnan' function. */ +#define HAVE_ISNAN 1 + /* Define if on AIX 3. System headers sometimes define this. We just want to avoid a redefinition error message. */ diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index bee30fdfee1..ab0c3f54fe1 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -700,6 +700,10 @@ + + diff --git a/PCbuild8/pythoncore/pythoncore.vcproj b/PCbuild8/pythoncore/pythoncore.vcproj index ec639b46d24..2f5484d8c0c 100644 --- a/PCbuild8/pythoncore/pythoncore.vcproj +++ b/PCbuild8/pythoncore/pythoncore.vcproj @@ -735,6 +735,10 @@ RelativePath="..\..\Python\pystate.c" > + + @@ -1193,6 +1197,10 @@ RelativePath="..\..\Include\pystate.h" > + + diff --git a/PCbuild9/pythoncore.vcproj b/PCbuild9/pythoncore.vcproj index 47cff8cd80b..ff270521915 100644 --- a/PCbuild9/pythoncore.vcproj +++ b/PCbuild9/pythoncore.vcproj @@ -874,6 +874,10 @@ RelativePath="..\Include\pystate.h" > + + @@ -1714,6 +1718,10 @@ RelativePath="..\Python\pystate.c" > + + diff --git a/Python/pystrcmp.c b/Python/pystrcmp.c new file mode 100644 index 00000000000..0012ef33bc3 --- /dev/null +++ b/Python/pystrcmp.c @@ -0,0 +1,25 @@ +/* Cross platform case insenstive string compare functions + */ + +#include "Python.h" + +int +PyOS_mystrnicmp(const char *s1, const char *s2, Py_ssize_t size) +{ + if (size == 0) + return 0; + while ((--size > 0) && (tolower(*s1) == tolower(*s2))) { + if (!*s1++ || !*s2++) + break; + } + return tolower(*s1) - tolower(*s2); +} + +int +PyOS_mystricmp(const char *s1, const char *s2) +{ + while (*s1 && (tolower(*s1++) == tolower(*s2++))) { + ; + } + return (tolower(*s1) - tolower(*s2)); +} diff --git a/configure b/configure index f31069e88ab..e60d61553de 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 59484 . +# From configure.in Revision: 59533 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61 for python 2.6. # @@ -20368,6 +20368,9 @@ echo "${ECHO_T}default LIBC=\"$LIBC\"" >&6; } fi +# ************************************ +# * Check for mathematical functions * +# ************************************ # check for hypot() in math library LIBS_SAVE=$LIBS LIBS="$LIBS $LIBM" @@ -20473,6 +20476,105 @@ fi done + + + + + +for ac_func in copysign isfinite isnan isinf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + LIBS=$LIBS_SAVE # check for wchar.h diff --git a/configure.in b/configure.in index 799499bc36b..0a2b17af8da 100644 --- a/configure.in +++ b/configure.in @@ -2974,10 +2974,16 @@ else AC_MSG_ERROR([proper usage is --with-libc=STRING]) fi], [AC_MSG_RESULT(default LIBC="$LIBC")]) +# ************************************ +# * Check for mathematical functions * +# ************************************ # check for hypot() in math library LIBS_SAVE=$LIBS LIBS="$LIBS $LIBM" AC_REPLACE_FUNCS(hypot) + +AC_CHECK_FUNCS(copysign isfinite isnan isinf) + LIBS=$LIBS_SAVE # check for wchar.h diff --git a/pyconfig.h.in b/pyconfig.h.in index 1ba3e5834ec..26197fb01b4 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -85,6 +85,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_CONIO_H +/* Define to 1 if you have the `copysign' function. */ +#undef HAVE_COPYSIGN + /* Define to 1 if you have the `ctermid' function. */ #undef HAVE_CTERMID @@ -288,6 +291,15 @@ /* Define to 1 if you have the header file. */ #undef HAVE_IO_H +/* Define to 1 if you have the `isfinite' function. */ +#undef HAVE_ISFINITE + +/* Define to 1 if you have the `isinf' function. */ +#undef HAVE_ISINF + +/* Define to 1 if you have the `isnan' function. */ +#undef HAVE_ISNAN + /* Define to 1 if you have the `kill' function. */ #undef HAVE_KILL @@ -1028,3 +1040,4 @@ #endif /*Py_PYCONFIG_H*/ +