Issue #5981: Fix some float.fromhex bugs related to inf and nan handling.
This commit is contained in:
parent
68e27eb834
commit
b1d45856c2
|
@ -462,6 +462,11 @@ class HexFloatTestCase(unittest.TestCase):
|
|||
'snan',
|
||||
'NaNs',
|
||||
'nna',
|
||||
'an',
|
||||
'nf',
|
||||
'nfinity',
|
||||
'inity',
|
||||
'iinity',
|
||||
'0xnan',
|
||||
'',
|
||||
' ',
|
||||
|
@ -510,6 +515,32 @@ class HexFloatTestCase(unittest.TestCase):
|
|||
'got %r instead' % (x, result))
|
||||
|
||||
|
||||
def test_whitespace(self):
|
||||
value_pairs = [
|
||||
('inf', INF),
|
||||
('-Infinity', -INF),
|
||||
('nan', NAN),
|
||||
('1.0', 1.0),
|
||||
('-0x.2', -0.125),
|
||||
('-0.0', -0.0)
|
||||
]
|
||||
whitespace = [
|
||||
'',
|
||||
' ',
|
||||
'\t',
|
||||
'\n',
|
||||
'\n \t',
|
||||
'\f',
|
||||
'\v',
|
||||
'\r'
|
||||
]
|
||||
for inp, expected in value_pairs:
|
||||
for lead in whitespace:
|
||||
for trail in whitespace:
|
||||
got = fromHex(lead + inp + trail)
|
||||
self.identical(got, expected)
|
||||
|
||||
|
||||
def test_from_hex(self):
|
||||
MIN = self.MIN;
|
||||
MAX = self.MAX;
|
||||
|
|
|
@ -12,6 +12,12 @@ What's New in Python 2.7 alpha 1
|
|||
Core and Builtins
|
||||
-----------------
|
||||
|
||||
- Issue #5981: Fix three minor inf/nan issues in float.fromhex:
|
||||
(1) inf and nan strings with trailing whitespace were incorrectly
|
||||
rejected; (2) parsing of strings representing infinities and nans
|
||||
was locale aware; and (3) the interpretation of fromhex('-nan')
|
||||
didn't match that of float('-nan').
|
||||
|
||||
- Issue #5920: For float.__format__, change the behavior with the
|
||||
empty presentation type (that is, not one of 'e', 'f', 'g', or 'n')
|
||||
to be like 'g' but with at least one decimal point and with a
|
||||
|
|
|
@ -1159,6 +1159,20 @@ Return a hexadecimal representation of a floating-point number.\n\
|
|||
>>> 3.14159.hex()\n\
|
||||
'0x1.921f9f01b866ep+1'");
|
||||
|
||||
/* Case-insensitive locale-independent string match used for nan and inf
|
||||
detection. t should be lower-case and null-terminated. Return a nonzero
|
||||
result if the first strlen(t) characters of s match t and 0 otherwise. */
|
||||
|
||||
static int
|
||||
case_insensitive_match(const char *s, const char *t)
|
||||
{
|
||||
while(*t && Py_TOLOWER(*s) == *t) {
|
||||
s++;
|
||||
t++;
|
||||
}
|
||||
return *t ? 0 : 1;
|
||||
}
|
||||
|
||||
/* Convert a hexadecimal string to a float. */
|
||||
|
||||
static PyObject *
|
||||
|
@ -1235,13 +1249,20 @@ float_fromhex(PyObject *cls, PyObject *arg)
|
|||
s++;
|
||||
|
||||
/* infinities and nans */
|
||||
if (PyOS_strnicmp(s, "nan", 4) == 0) {
|
||||
x = Py_NAN;
|
||||
if (*s == 'i' || *s == 'I') {
|
||||
if (!case_insensitive_match(s+1, "nf"))
|
||||
goto parse_error;
|
||||
s += 3;
|
||||
x = Py_HUGE_VAL;
|
||||
if (case_insensitive_match(s, "inity"))
|
||||
s += 5;
|
||||
goto finished;
|
||||
}
|
||||
if (PyOS_strnicmp(s, "inf", 4) == 0 ||
|
||||
PyOS_strnicmp(s, "infinity", 9) == 0) {
|
||||
x = sign*Py_HUGE_VAL;
|
||||
if (*s == 'n' || *s == 'N') {
|
||||
if (!case_insensitive_match(s+1, "an"))
|
||||
goto parse_error;
|
||||
s += 3;
|
||||
x = Py_NAN;
|
||||
goto finished;
|
||||
}
|
||||
|
||||
|
@ -1294,12 +1315,6 @@ float_fromhex(PyObject *cls, PyObject *arg)
|
|||
else
|
||||
exp = 0;
|
||||
|
||||
/* optional trailing whitespace leading to the end of the string */
|
||||
while (Py_ISSPACE(*s))
|
||||
s++;
|
||||
if (s != s_end)
|
||||
goto parse_error;
|
||||
|
||||
/* for 0 <= j < ndigits, HEX_DIGIT(j) gives the jth most significant digit */
|
||||
#define HEX_DIGIT(j) hex_from_char(*((j) < fdigits ? \
|
||||
coeff_end-(j) : \
|
||||
|
@ -1313,7 +1328,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
|
|||
while (ndigits > 0 && HEX_DIGIT(ndigits-1) == 0)
|
||||
ndigits--;
|
||||
if (ndigits == 0 || exp < LONG_MIN/2) {
|
||||
x = sign * 0.0;
|
||||
x = 0.0;
|
||||
goto finished;
|
||||
}
|
||||
if (exp > LONG_MAX/2)
|
||||
|
@ -1329,7 +1344,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
|
|||
|
||||
/* catch almost all nonextreme cases of overflow and underflow here */
|
||||
if (top_exp < DBL_MIN_EXP - DBL_MANT_DIG) {
|
||||
x = sign * 0.0;
|
||||
x = 0.0;
|
||||
goto finished;
|
||||
}
|
||||
if (top_exp > DBL_MAX_EXP)
|
||||
|
@ -1344,7 +1359,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
|
|||
/* no rounding required */
|
||||
for (i = ndigits-1; i >= 0; i--)
|
||||
x = 16.0*x + HEX_DIGIT(i);
|
||||
x = sign * ldexp(x, (int)(exp));
|
||||
x = ldexp(x, (int)(exp));
|
||||
goto finished;
|
||||
}
|
||||
/* rounding required. key_digit is the index of the hex digit
|
||||
|
@ -1378,10 +1393,15 @@ float_fromhex(PyObject *cls, PyObject *arg)
|
|||
goto overflow_error;
|
||||
}
|
||||
}
|
||||
x = sign * ldexp(x, (int)(exp+4*key_digit));
|
||||
x = ldexp(x, (int)(exp+4*key_digit));
|
||||
|
||||
finished:
|
||||
result_as_float = Py_BuildValue("(d)", x);
|
||||
/* optional trailing whitespace leading to the end of the string */
|
||||
while (Py_ISSPACE(*s))
|
||||
s++;
|
||||
if (s != s_end)
|
||||
goto parse_error;
|
||||
result_as_float = Py_BuildValue("(d)", sign * x);
|
||||
if (result_as_float == NULL)
|
||||
return NULL;
|
||||
result = PyObject_CallObject(cls, result_as_float);
|
||||
|
|
Loading…
Reference in New Issue