Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0a8143f

Browse files
committed
Applied patch #1635: Float patch for inf and nan on Windows (and other platforms).
The patch unifies float("inf") and repr(float("inf")) on all platforms.
1 parent 8777bca commit 0a8143f

18 files changed

+394
-9
lines changed

Doc/c-api/utilities.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ The following functions provide locale-independent string to number conversions.
10661066

10671067
.. versionadded:: 2.4
10681068

1069-
1069+
10701070
.. cfunction:: double PyOS_ascii_atof(const char *nptr)
10711071

10721072
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.
10751075

10761076
See the Unix man page :manpage:`atof(2)` for details.
10771077

1078+
1079+
.. cfunction:: char * PyOS_stricmp(char *s1, char *s2)
1080+
1081+
Case insensitive comparsion of strings. The functions works almost
1082+
identical to :cfunc:`strcmp` except that it ignores the case.
1083+
1084+
.. versionadded:: 2.6
1085+
1086+
1087+
.. cfunction:: char * PyOS_strnicmp(char *s1, char *s2, Py_ssize_t size)
1088+
1089+
Case insensitive comparsion of strings. The functions works almost
1090+
identical to :cfunc:`strncmp` except that it ignores the case.
1091+
1092+
.. versionadded:: 2.6
1093+
10781094

10791095
.. _reflection:
10801096

Doc/library/functions.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,8 @@ available. They are listed here in alphabetical order.
434434

435435
Convert a string or a number to floating point. If the argument is a string, it
436436
must contain a possibly signed decimal or floating point number, possibly
437-
embedded in whitespace. Otherwise, the argument may be a plain or long integer
437+
embedded in whitespace. The argument may also be [+|-]nan or [+|-]inf.
438+
Otherwise, the argument may be a plain or long integer
438439
or a floating point number, and a floating point number with the same value
439440
(within Python's floating point precision) is returned. If no argument is
440441
given, returns ``0.0``.
@@ -446,9 +447,10 @@ available. They are listed here in alphabetical order.
446447
single: Infinity
447448

448449
When passing in a string, values for NaN and Infinity may be returned, depending
449-
on the underlying C library. The specific set of strings accepted which cause
450-
these values to be returned depends entirely on the C library and is known to
451-
vary.
450+
on the underlying C library. Float accepts the strings nan, inf and -inf for
451+
NaN and positive or negative infinity. The case and a leading + are ignored as
452+
well as a leading - is ignored for NaN. Float always represents NaN and infinity
453+
as nan, inf or -inf.
452454

453455
The float type is described in :ref:`typesnumeric`.
454456

Doc/library/stdtypes.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ numeric operations have a higher priority than comparison operations):
298298
+--------------------+---------------------------------+--------+
299299
| ``long(x)`` | *x* converted to long integer | \(2) |
300300
+--------------------+---------------------------------+--------+
301-
| ``float(x)`` | *x* converted to floating point | |
301+
| ``float(x)`` | *x* converted to floating point | \(6) |
302302
+--------------------+---------------------------------+--------+
303303
| ``complex(re,im)`` | a complex number with real part | |
304304
| | *re*, imaginary part *im*. | |
@@ -355,6 +355,13 @@ Notes:
355355
Also referred to as integer division. The resultant value is a whole integer,
356356
though the result's type is not necessarily int.
357357

358+
(6)
359+
float also accepts the strings "nan" and "inf" with an optional prefix "+"
360+
or "-" for Not a Number (NaN) and positive or negative infinity.
361+
362+
.. versionadded:: 2.6
363+
364+
358365
.. % XXXJH exceptions: overflow (when? what operations?) zerodivision
359366
360367

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
#include "eval.h"
130130

131131
#include "pystrtod.h"
132+
#include "pystrcmp.h"
132133

133134
/* _Py_Mangle is defined in compile.c */
134135
PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);

Include/pyport.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,17 @@ extern "C" {
349349
#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE)
350350
#endif
351351

352+
/* High precision defintion of pi and e (Euler)
353+
* The values are taken from libc6's math.h.
354+
*/
355+
#ifndef Py_MATH_PI
356+
#define Py_MATH_PI 3.1415926535897932384626433832795029L
357+
#endif
358+
359+
#ifndef Py_MATH_E
360+
#define Py_MATH_E 2.7182818284590452353602874713526625L
361+
#endif
362+
352363
/* Py_IS_NAN(X)
353364
* Return 1 if float or double arg is a NaN, else 0.
354365
* Caution:
@@ -358,8 +369,12 @@ extern "C" {
358369
* a platform where it doesn't work.
359370
*/
360371
#ifndef Py_IS_NAN
372+
#ifdef HAVE_ISNAN
373+
#define Py_IS_NAN(X) isnan(X)
374+
#else
361375
#define Py_IS_NAN(X) ((X) != (X))
362376
#endif
377+
#endif
363378

364379
/* Py_IS_INFINITY(X)
365380
* Return 1 if float or double arg is an infinity, else 0.
@@ -370,17 +385,25 @@ extern "C" {
370385
* Override in pyconfig.h if you have a better spelling on your platform.
371386
*/
372387
#ifndef Py_IS_INFINITY
388+
#ifdef HAVE_ISINF
389+
#define Py_IS_INFINITY(X) isinf(X)
390+
#else
373391
#define Py_IS_INFINITY(X) ((X) && (X)*0.5 == (X))
374392
#endif
393+
#endif
375394

376395
/* Py_IS_FINITE(X)
377396
* Return 1 if float or double arg is neither infinite nor NAN, else 0.
378397
* Some compilers (e.g. VisualStudio) have intrisics for this, so a special
379398
* macro for this particular test is useful
380399
*/
381400
#ifndef Py_IS_FINITE
401+
#ifdef HAVE_ISFINITE
402+
#define Py_IS_FINITE(X) isfinite
403+
#else
382404
#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X))
383405
#endif
406+
#endif
384407

385408
/* HUGE_VAL is supposed to expand to a positive double infinity. Python
386409
* uses Py_HUGE_VAL instead because some platforms are broken in this
@@ -393,6 +416,15 @@ extern "C" {
393416
#define Py_HUGE_VAL HUGE_VAL
394417
#endif
395418

419+
/* Py_NAN
420+
* A value that evaluates to a NaN. On IEEE 754 platforms INF*0 or
421+
* INF/INF works. Define Py_NO_NAN in pyconfig.h if your platform
422+
* doesn't support NaNs.
423+
*/
424+
#if !defined(Py_NAN) && !defined(Py_NO_NAN)
425+
#define Py_NAN (Py_HUGE_VAL * 0.)
426+
#endif
427+
396428
/* Py_OVERFLOWED(X)
397429
* Return 1 iff a libm function overflowed. Set errno to 0 before calling
398430
* a libm function, and invoke this macro after, passing the function

Include/pystrcmp.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef Py_STRCMP_H
2+
#define Py_STRCMP_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
PyAPI_FUNC(int) PyOS_mystrnicmp(const char *, const char *, Py_ssize_t);
9+
PyAPI_FUNC(int) PyOS_mystricmp(const char *, const char *);
10+
11+
#ifdef MS_WINDOWS
12+
#define PyOS_strnicmp strnicmp
13+
#define PyOS_stricmp stricmp
14+
#else
15+
#define PyOS_strnicmp PyOS_mystrnicmp
16+
#define PyOS_stricmp PyOS_mystricmp
17+
#endif
18+
19+
#ifdef __cplusplus
20+
}
21+
#endif
22+
23+
#endif /* !Py_STRCMP_H */

Lib/test/test_float.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
import os
44
from test import test_support
55

6+
def isinf(x):
7+
return x * 0.5 == x
8+
9+
def isnan(x):
10+
return x != x
11+
612
class FormatFunctionsTestCase(unittest.TestCase):
713

814
def setUp(self):
@@ -128,13 +134,78 @@ def test_repr(self):
128134
self.assertEqual(v, eval(repr(v)))
129135
floats_file.close()
130136

137+
# Beginning with Python 2.6 float has cross platform compatible
138+
# ways to create and representate inf and nan
139+
class InfNanTest(unittest.TestCase):
140+
def test_inf_from_str(self):
141+
self.assert_(isinf(float("inf")))
142+
self.assert_(isinf(float("+inf")))
143+
self.assert_(isinf(float("-inf")))
144+
145+
self.assertEqual(repr(float("inf")), "inf")
146+
self.assertEqual(repr(float("+inf")), "inf")
147+
self.assertEqual(repr(float("-inf")), "-inf")
148+
149+
self.assertEqual(repr(float("INF")), "inf")
150+
self.assertEqual(repr(float("+Inf")), "inf")
151+
self.assertEqual(repr(float("-iNF")), "-inf")
152+
153+
self.assertEqual(str(float("inf")), "inf")
154+
self.assertEqual(str(float("+inf")), "inf")
155+
self.assertEqual(str(float("-inf")), "-inf")
156+
157+
self.assertRaises(ValueError, float, "info")
158+
self.assertRaises(ValueError, float, "+info")
159+
self.assertRaises(ValueError, float, "-info")
160+
self.assertRaises(ValueError, float, "in")
161+
self.assertRaises(ValueError, float, "+in")
162+
self.assertRaises(ValueError, float, "-in")
163+
164+
def test_inf_as_str(self):
165+
self.assertEqual(repr(1e300 * 1e300), "inf")
166+
self.assertEqual(repr(-1e300 * 1e300), "-inf")
167+
168+
self.assertEqual(str(1e300 * 1e300), "inf")
169+
self.assertEqual(str(-1e300 * 1e300), "-inf")
170+
171+
def test_nan_from_str(self):
172+
self.assert_(isnan(float("nan")))
173+
self.assert_(isnan(float("+nan")))
174+
self.assert_(isnan(float("-nan")))
175+
176+
self.assertEqual(repr(float("nan")), "nan")
177+
self.assertEqual(repr(float("+nan")), "nan")
178+
self.assertEqual(repr(float("-nan")), "nan")
179+
180+
self.assertEqual(repr(float("NAN")), "nan")
181+
self.assertEqual(repr(float("+NAn")), "nan")
182+
self.assertEqual(repr(float("-NaN")), "nan")
183+
184+
self.assertEqual(str(float("nan")), "nan")
185+
self.assertEqual(str(float("+nan")), "nan")
186+
self.assertEqual(str(float("-nan")), "nan")
187+
188+
self.assertRaises(ValueError, float, "nana")
189+
self.assertRaises(ValueError, float, "+nana")
190+
self.assertRaises(ValueError, float, "-nana")
191+
self.assertRaises(ValueError, float, "na")
192+
self.assertRaises(ValueError, float, "+na")
193+
self.assertRaises(ValueError, float, "-na")
194+
195+
def test_nan_as_str(self):
196+
self.assertEqual(repr(1e300 * 1e300 * 0), "nan")
197+
self.assertEqual(repr(-1e300 * 1e300 * 0), "nan")
198+
199+
self.assertEqual(str(1e300 * 1e300 * 0), "nan")
200+
self.assertEqual(str(-1e300 * 1e300 * 0), "nan")
131201

132202
def test_main():
133203
test_support.run_unittest(
134204
FormatFunctionsTestCase,
135205
UnknownFormatTestCase,
136206
IEEEFormatTestCase,
137-
#ReprTestCase
207+
ReprTestCase,
208+
InfNanTest,
138209
)
139210

140211
if __name__ == '__main__':

Makefile.pre.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ PYTHON_OBJS= \
275275
Python/sysmodule.o \
276276
Python/traceback.o \
277277
Python/getopt.o \
278+
Python/pystrcmp.o \
278279
Python/pystrtod.o \
279280
Python/$(DYNLOADFILE) \
280281
$(LIBOBJS) \
@@ -554,6 +555,8 @@ PYTHON_HEADERS= \
554555
Include/pymem.h \
555556
Include/pyport.h \
556557
Include/pystate.h \
558+
Include/pystrtod.h \
559+
Include/pystrcmp.h \
557560
Include/pythonrun.h \
558561
Include/rangeobject.h \
559562
Include/setobject.h \

Misc/NEWS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ What's New in Python 2.6 alpha 1?
1212
Core and builtins
1313
-----------------
1414

15+
- Issue #1635: Platform independent creation and representation of NaN
16+
and INF. float("nan"), float("inf") and float("-inf") now work on every
17+
platform with IEEE 754 semantics.
18+
19+
- Added case insensitive comparsion methods ``PyOS_stricmp(char*, char*)``
20+
and ``PyOS_strnicmp(char*, char*, Py_ssize_t)``.
21+
1522
- Compiler now generates simpler and faster code for dictionary literals.
1623
The oparg for BUILD_MAP now indicates an estimated dictionary size.
1724
There is a new opcode, STORE_MAP, for adding entries to the dictionary.

Objects/floatobject.c

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ still supported but now *officially* useless: if pend is not NULL,
128128
PyObject *
129129
PyFloat_FromString(PyObject *v, char **pend)
130130
{
131-
const char *s, *last, *end;
131+
const char *s, *last, *end, *sp;
132132
double x;
133133
char buffer[256]; /* for errors */
134134
#ifdef Py_USING_UNICODE
@@ -171,6 +171,7 @@ PyFloat_FromString(PyObject *v, char **pend)
171171
PyErr_SetString(PyExc_ValueError, "empty string for float()");
172172
return NULL;
173173
}
174+
sp = s;
174175
/* We don't care about overflow or underflow. If the platform supports
175176
* them, infinities and signed zeroes (on underflow) are fine.
176177
* However, strtod can return 0 for denormalized numbers, where atof
@@ -186,7 +187,26 @@ PyFloat_FromString(PyObject *v, char **pend)
186187
byte at the end of the string, when the input is inf(inity). */
187188
if (end > last)
188189
end = last;
190+
/* Check for inf and nan. This is done late because it rarely happens. */
189191
if (end == s) {
192+
char *p = (char*)sp;
193+
int sign = 1;
194+
195+
if (*p == '-') {
196+
sign = -1;
197+
p++;
198+
}
199+
if (*p == '+') {
200+
p++;
201+
}
202+
if (PyOS_strnicmp(p, "inf", 4) == 0) {
203+
return PyFloat_FromDouble(sign * Py_HUGE_VAL);
204+
}
205+
#ifdef Py_NAN
206+
if(PyOS_strnicmp(p, "nan", 4) == 0) {
207+
return PyFloat_FromDouble(Py_NAN);
208+
}
209+
#endif
190210
PyOS_snprintf(buffer, sizeof(buffer),
191211
"invalid literal for float(): %.200s", s);
192212
PyErr_SetString(PyExc_ValueError, buffer);
@@ -271,6 +291,8 @@ format_float(char *buf, size_t buflen, PyFloatObject *v, int precision)
271291
{
272292
register char *cp;
273293
char format[32];
294+
int i;
295+
274296
/* Subroutine for float_repr and float_print.
275297
We want float numbers to be recognizable as such,
276298
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)
293315
*cp++ = '.';
294316
*cp++ = '0';
295317
*cp++ = '\0';
318+
return;
296319
}
320+
/* Checking the next three chars should be more than enough to
321+
* detect inf or nan, even on Windows. We check for inf or nan
322+
* at last because they are rare cases.
323+
*/
324+
for (i=0; *cp != '\0' && i<3; cp++, i++) {
325+
if (isdigit(Py_CHARMASK(*cp)) || *cp == '.')
326+
continue;
327+
/* found something that is neither a digit nor point
328+
* it might be a NaN or INF
329+
*/
330+
#ifdef Py_NAN
331+
if (Py_IS_NAN(v->ob_fval)) {
332+
strcpy(buf, "nan");
333+
}
334+
else
335+
#endif
336+
if (Py_IS_INFINITY(v->ob_fval)) {
337+
cp = buf;
338+
if (*cp == '-')
339+
cp++;
340+
strcpy(cp, "inf");
341+
}
342+
break;
343+
}
344+
297345
}
298346

299347
/* XXX PyFloat_AsStringEx should not be a public API function (for one

0 commit comments

Comments
 (0)