Skip to content

Commit ce05717

Browse files
committed
Merged revisions 72564 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r72564 | mark.dickinson | 2009-05-11 16:33:08 +0100 (Mon, 11 May 2009) | 2 lines Issue python#5981: Fix some float.fromhex bugs related to inf and nan handling. ........
1 parent fd9258d commit ce05717

3 files changed

Lines changed: 73 additions & 17 deletions

File tree

Lib/test/test_float.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,11 @@ def test_invalid_inputs(self):
391391
'snan',
392392
'NaNs',
393393
'nna',
394+
'an',
395+
'nf',
396+
'nfinity',
397+
'inity',
398+
'iinity',
394399
'0xnan',
395400
'',
396401
' ',
@@ -439,6 +444,32 @@ def test_invalid_inputs(self):
439444
'got %r instead' % (x, result))
440445

441446

447+
def test_whitespace(self):
448+
value_pairs = [
449+
('inf', INF),
450+
('-Infinity', -INF),
451+
('nan', NAN),
452+
('1.0', 1.0),
453+
('-0x.2', -0.125),
454+
('-0.0', -0.0)
455+
]
456+
whitespace = [
457+
'',
458+
' ',
459+
'\t',
460+
'\n',
461+
'\n \t',
462+
'\f',
463+
'\v',
464+
'\r'
465+
]
466+
for inp, expected in value_pairs:
467+
for lead in whitespace:
468+
for trail in whitespace:
469+
got = fromHex(lead + inp + trail)
470+
self.identical(got, expected)
471+
472+
442473
def test_from_hex(self):
443474
MIN = self.MIN;
444475
MAX = self.MAX;

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ What's New in Python 2.6.3
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #5981: Fix two minor inf/nan issues in float.fromhex: (1) inf
16+
and nan strings with trailing whitespace were incorrectly rejected
17+
and (2) the interpretation of fromhex('-nan') didn't match that of
18+
float('-nan').
19+
1520
- Issue #5890: in subclasses of 'property' the __doc__ attribute was
1621
shadowed by classtype's, even if it was None. property now
1722
inserts the __doc__ into the subclass instance __dict__.

Objects/floatobject.c

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,20 @@ Return a hexadecimal representation of a floating-point number.\n\
12631263
>>> 3.14159.hex()\n\
12641264
'0x1.921f9f01b866ep+1'");
12651265

1266+
/* Case-insensitive string match used for nan and inf detection. t should be
1267+
lower-case and null-terminated. Return a nonzero result if the first
1268+
strlen(t) characters of s match t and 0 otherwise. */
1269+
1270+
static int
1271+
case_insensitive_match(const char *s, const char *t)
1272+
{
1273+
while(*t && tolower(*s) == *t) {
1274+
s++;
1275+
t++;
1276+
}
1277+
return *t ? 0 : 1;
1278+
}
1279+
12661280
/* Convert a hexadecimal string to a float. */
12671281

12681282
static PyObject *
@@ -1329,7 +1343,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
13291343
********************/
13301344

13311345
/* leading whitespace and optional sign */
1332-
while (isspace(*s))
1346+
while (*s && isspace(Py_CHARMASK(*s)))
13331347
s++;
13341348
if (*s == '-') {
13351349
s++;
@@ -1339,13 +1353,20 @@ float_fromhex(PyObject *cls, PyObject *arg)
13391353
s++;
13401354

13411355
/* infinities and nans */
1342-
if (PyOS_strnicmp(s, "nan", 4) == 0) {
1343-
x = Py_NAN;
1356+
if (*s == 'i' || *s == 'I') {
1357+
if (!case_insensitive_match(s+1, "nf"))
1358+
goto parse_error;
1359+
s += 3;
1360+
x = Py_HUGE_VAL;
1361+
if (case_insensitive_match(s, "inity"))
1362+
s += 5;
13441363
goto finished;
13451364
}
1346-
if (PyOS_strnicmp(s, "inf", 4) == 0 ||
1347-
PyOS_strnicmp(s, "infinity", 9) == 0) {
1348-
x = sign*Py_HUGE_VAL;
1365+
if (*s == 'n' || *s == 'N') {
1366+
if (!case_insensitive_match(s+1, "an"))
1367+
goto parse_error;
1368+
s += 3;
1369+
x = Py_NAN;
13491370
goto finished;
13501371
}
13511372

@@ -1398,12 +1419,6 @@ float_fromhex(PyObject *cls, PyObject *arg)
13981419
else
13991420
exp = 0;
14001421

1401-
/* optional trailing whitespace leading to the end of the string */
1402-
while (isspace(*s))
1403-
s++;
1404-
if (s != s_end)
1405-
goto parse_error;
1406-
14071422
/* for 0 <= j < ndigits, HEX_DIGIT(j) gives the jth most significant digit */
14081423
#define HEX_DIGIT(j) hex_from_char(*((j) < fdigits ? \
14091424
coeff_end-(j) : \
@@ -1417,7 +1432,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
14171432
while (ndigits > 0 && HEX_DIGIT(ndigits-1) == 0)
14181433
ndigits--;
14191434
if (ndigits == 0 || exp < LONG_MIN/2) {
1420-
x = sign * 0.0;
1435+
x = 0.0;
14211436
goto finished;
14221437
}
14231438
if (exp > LONG_MAX/2)
@@ -1433,7 +1448,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
14331448

14341449
/* catch almost all nonextreme cases of overflow and underflow here */
14351450
if (top_exp < DBL_MIN_EXP - DBL_MANT_DIG) {
1436-
x = sign * 0.0;
1451+
x = 0.0;
14371452
goto finished;
14381453
}
14391454
if (top_exp > DBL_MAX_EXP)
@@ -1448,7 +1463,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
14481463
/* no rounding required */
14491464
for (i = ndigits-1; i >= 0; i--)
14501465
x = 16.0*x + HEX_DIGIT(i);
1451-
x = sign * ldexp(x, (int)(exp));
1466+
x = ldexp(x, (int)(exp));
14521467
goto finished;
14531468
}
14541469
/* rounding required. key_digit is the index of the hex digit
@@ -1482,10 +1497,15 @@ float_fromhex(PyObject *cls, PyObject *arg)
14821497
goto overflow_error;
14831498
}
14841499
}
1485-
x = sign * ldexp(x, (int)(exp+4*key_digit));
1500+
x = ldexp(x, (int)(exp+4*key_digit));
14861501

14871502
finished:
1488-
result_as_float = Py_BuildValue("(d)", x);
1503+
/* optional trailing whitespace leading to the end of the string */
1504+
while (*s && isspace(Py_CHARMASK(*s)))
1505+
s++;
1506+
if (s != s_end)
1507+
goto parse_error;
1508+
result_as_float = Py_BuildValue("(d)", sign * x);
14891509
if (result_as_float == NULL)
14901510
return NULL;
14911511
result = PyObject_CallObject(cls, result_as_float);

0 commit comments

Comments
 (0)