Issue #5981: Fix some float.fromhex bugs related to inf and nan handling.

This commit is contained in:
Mark Dickinson 2009-05-11 15:33:08 +00:00
parent 68e27eb834
commit b1d45856c2
3 changed files with 73 additions and 16 deletions

View File

@ -462,6 +462,11 @@ class HexFloatTestCase(unittest.TestCase):
'snan', 'snan',
'NaNs', 'NaNs',
'nna', 'nna',
'an',
'nf',
'nfinity',
'inity',
'iinity',
'0xnan', '0xnan',
'', '',
' ', ' ',
@ -510,6 +515,32 @@ class HexFloatTestCase(unittest.TestCase):
'got %r instead' % (x, result)) '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): def test_from_hex(self):
MIN = self.MIN; MIN = self.MIN;
MAX = self.MAX; MAX = self.MAX;

View File

@ -12,6 +12,12 @@ What's New in Python 2.7 alpha 1
Core and Builtins 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 - Issue #5920: For float.__format__, change the behavior with the
empty presentation type (that is, not one of 'e', 'f', 'g', or 'n') 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 to be like 'g' but with at least one decimal point and with a

View File

@ -1159,6 +1159,20 @@ Return a hexadecimal representation of a floating-point number.\n\
>>> 3.14159.hex()\n\ >>> 3.14159.hex()\n\
'0x1.921f9f01b866ep+1'"); '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. */ /* Convert a hexadecimal string to a float. */
static PyObject * static PyObject *
@ -1235,13 +1249,20 @@ float_fromhex(PyObject *cls, PyObject *arg)
s++; s++;
/* infinities and nans */ /* infinities and nans */
if (PyOS_strnicmp(s, "nan", 4) == 0) { if (*s == 'i' || *s == 'I') {
x = Py_NAN; 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; goto finished;
} }
if (PyOS_strnicmp(s, "inf", 4) == 0 || if (*s == 'n' || *s == 'N') {
PyOS_strnicmp(s, "infinity", 9) == 0) { if (!case_insensitive_match(s+1, "an"))
x = sign*Py_HUGE_VAL; goto parse_error;
s += 3;
x = Py_NAN;
goto finished; goto finished;
} }
@ -1294,12 +1315,6 @@ float_fromhex(PyObject *cls, PyObject *arg)
else else
exp = 0; 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 */ /* for 0 <= j < ndigits, HEX_DIGIT(j) gives the jth most significant digit */
#define HEX_DIGIT(j) hex_from_char(*((j) < fdigits ? \ #define HEX_DIGIT(j) hex_from_char(*((j) < fdigits ? \
coeff_end-(j) : \ coeff_end-(j) : \
@ -1313,7 +1328,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
while (ndigits > 0 && HEX_DIGIT(ndigits-1) == 0) while (ndigits > 0 && HEX_DIGIT(ndigits-1) == 0)
ndigits--; ndigits--;
if (ndigits == 0 || exp < LONG_MIN/2) { if (ndigits == 0 || exp < LONG_MIN/2) {
x = sign * 0.0; x = 0.0;
goto finished; goto finished;
} }
if (exp > LONG_MAX/2) 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 */ /* catch almost all nonextreme cases of overflow and underflow here */
if (top_exp < DBL_MIN_EXP - DBL_MANT_DIG) { if (top_exp < DBL_MIN_EXP - DBL_MANT_DIG) {
x = sign * 0.0; x = 0.0;
goto finished; goto finished;
} }
if (top_exp > DBL_MAX_EXP) if (top_exp > DBL_MAX_EXP)
@ -1344,7 +1359,7 @@ float_fromhex(PyObject *cls, PyObject *arg)
/* no rounding required */ /* no rounding required */
for (i = ndigits-1; i >= 0; i--) for (i = ndigits-1; i >= 0; i--)
x = 16.0*x + HEX_DIGIT(i); x = 16.0*x + HEX_DIGIT(i);
x = sign * ldexp(x, (int)(exp)); x = ldexp(x, (int)(exp));
goto finished; goto finished;
} }
/* rounding required. key_digit is the index of the hex digit /* rounding required. key_digit is the index of the hex digit
@ -1378,10 +1393,15 @@ float_fromhex(PyObject *cls, PyObject *arg)
goto overflow_error; goto overflow_error;
} }
} }
x = sign * ldexp(x, (int)(exp+4*key_digit)); x = ldexp(x, (int)(exp+4*key_digit));
finished: 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) if (result_as_float == NULL)
return NULL; return NULL;
result = PyObject_CallObject(cls, result_as_float); result = PyObject_CallObject(cls, result_as_float);