mirror of
https://github.com/AdaCore/cpython.git
synced 2026-02-12 12:57:15 -08:00
This was a misleading bug -- the true "bug" was that hash(x) gave an error return when x is an infinity. Fixed that. Added new Py_IS_INFINITY macro to pyport.h. Rearranged code to reduce growing duplication in hashing of float and complex numbers, pushing Trent's earlier stab at that to a logical conclusion. Fixed exceedingly rare bug where hashing of floats could return -1 even if there wasn't an error (didn't waste time trying to construct a test case, it was simply obvious from the code that it *could* happen). Improved complex hash so that hash(complex(x, y)) doesn't systematically equal hash(complex(y, x)) anymore.
This commit is contained in:
@@ -103,7 +103,7 @@ extern "C" {
|
||||
#define Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) ((I) >> (J))
|
||||
#endif
|
||||
|
||||
/* Py_FORCE_EXPANSION
|
||||
/* Py_FORCE_EXPANSION(X)
|
||||
* "Simply" returns its argument. However, macro expansions within the
|
||||
* argument are evaluated. This unfortunate trickery is needed to get
|
||||
* token-pasting to work as desired in some cases.
|
||||
@@ -123,6 +123,14 @@ extern "C" {
|
||||
#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE)
|
||||
#endif
|
||||
|
||||
/* Py_IS_INFINITY(X)
|
||||
* Return 1 if float or double arg is an infinity, else 0.
|
||||
* Caution:
|
||||
* X is evaluated more than once.
|
||||
* This implementation may set the underflow flag if |X| is very small;
|
||||
* it really can't be implemented correctly (& easily) before C99.
|
||||
*/
|
||||
#define Py_IS_INFINITY(X) ((X) && (X)*0.5 == (X))
|
||||
|
||||
/**************************************************************************
|
||||
Prototypes that are missing from the standard include files on some systems
|
||||
|
||||
@@ -242,52 +242,23 @@ complex_compare(PyComplexObject *v, PyComplexObject *w)
|
||||
static long
|
||||
complex_hash(PyComplexObject *v)
|
||||
{
|
||||
double intpart, fractpart;
|
||||
long x;
|
||||
/* This is designed so that Python numbers with the same
|
||||
value hash to the same value, otherwise comparisons
|
||||
of mapping keys will turn out weird */
|
||||
|
||||
#ifdef MPW /* MPW C modf expects pointer to extended as second argument */
|
||||
{
|
||||
extended e;
|
||||
fractpart = modf(v->cval.real, &e);
|
||||
intpart = e;
|
||||
}
|
||||
#else
|
||||
fractpart = modf(v->cval.real, &intpart);
|
||||
#endif
|
||||
|
||||
if (fractpart == 0.0 && v->cval.imag == 0.0) {
|
||||
if (intpart > LONG_MAX || -intpart > LONG_MAX) {
|
||||
/* Convert to long int and use its hash... */
|
||||
PyObject *w = PyLong_FromDouble(v->cval.real);
|
||||
if (w == NULL)
|
||||
return -1;
|
||||
x = PyObject_Hash(w);
|
||||
Py_DECREF(w);
|
||||
return x;
|
||||
}
|
||||
x = (long)intpart;
|
||||
}
|
||||
else {
|
||||
x = _Py_HashDouble(v->cval.real);
|
||||
if (x == -1)
|
||||
return -1;
|
||||
|
||||
if (v->cval.imag != 0.0) { /* Hash the imaginary part */
|
||||
/* XXX Note that this hashes complex(x, y)
|
||||
to the same value as complex(y, x).
|
||||
Still better than it used to be :-) */
|
||||
long y = _Py_HashDouble(v->cval.imag);
|
||||
if (y == -1)
|
||||
return -1;
|
||||
x += y;
|
||||
}
|
||||
}
|
||||
if (x == -1)
|
||||
x = -2;
|
||||
return x;
|
||||
long hashreal, hashimag, combined;
|
||||
hashreal = _Py_HashDouble(v->cval.real);
|
||||
if (hashreal == -1)
|
||||
return -1;
|
||||
hashimag = _Py_HashDouble(v->cval.imag);
|
||||
if (hashimag == -1)
|
||||
return -1;
|
||||
/* Note: if the imaginary part is 0, hashimag is 0 now,
|
||||
* so the following returns hashreal unchanged. This is
|
||||
* important because numbers of different types that
|
||||
* compare equal must have the same hash value, so that
|
||||
* hash(x + 0*j) must equal hash(x).
|
||||
*/
|
||||
combined = hashreal + 1000003 * hashimag;
|
||||
if (combined == -1)
|
||||
combined = -2;
|
||||
return combined;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
||||
@@ -326,44 +326,7 @@ float_compare(PyFloatObject *v, PyFloatObject *w)
|
||||
static long
|
||||
float_hash(PyFloatObject *v)
|
||||
{
|
||||
double intpart, fractpart;
|
||||
long x;
|
||||
/* This is designed so that Python numbers with the same
|
||||
value hash to the same value, otherwise comparisons
|
||||
of mapping keys will turn out weird */
|
||||
|
||||
#ifdef MPW /* MPW C modf expects pointer to extended as second argument */
|
||||
{
|
||||
extended e;
|
||||
fractpart = modf(v->ob_fval, &e);
|
||||
intpart = e;
|
||||
}
|
||||
#else
|
||||
fractpart = modf(v->ob_fval, &intpart);
|
||||
#endif
|
||||
|
||||
if (fractpart == 0.0) {
|
||||
if (intpart > LONG_MAX || -intpart > LONG_MAX) {
|
||||
/* Convert to long int and use its hash... */
|
||||
PyObject *w = PyLong_FromDouble(v->ob_fval);
|
||||
if (w == NULL)
|
||||
return -1;
|
||||
x = PyObject_Hash(w);
|
||||
Py_DECREF(w);
|
||||
return x;
|
||||
}
|
||||
x = (long)intpart;
|
||||
}
|
||||
else {
|
||||
/* Note -- if you change this code, also change the copy
|
||||
in complexobject.c */
|
||||
x = _Py_HashDouble(v->ob_fval);
|
||||
if (x == -1)
|
||||
return -1;
|
||||
}
|
||||
if (x == -1)
|
||||
x = -2;
|
||||
return x;
|
||||
return _Py_HashDouble(v->ob_fval);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
||||
@@ -114,7 +114,7 @@ PyLong_FromDouble(double dval)
|
||||
double frac;
|
||||
int i, ndig, expo, neg;
|
||||
neg = 0;
|
||||
if (dval && dval * 0.5 == dval) {
|
||||
if (Py_IS_INFINITY(dval)) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"cannot convert float infinity to long");
|
||||
return NULL;
|
||||
|
||||
@@ -532,25 +532,65 @@ PyObject_Compare(PyObject *v, PyObject *w)
|
||||
long
|
||||
_Py_HashDouble(double v)
|
||||
{
|
||||
/* Use frexp to get at the bits in the double.
|
||||
double intpart, fractpart;
|
||||
int expo;
|
||||
long hipart;
|
||||
long x; /* the final hash value */
|
||||
/* This is designed so that Python numbers of different types
|
||||
* that compare equal hash to the same value; otherwise comparisons
|
||||
* of mapping keys will turn out weird.
|
||||
*/
|
||||
|
||||
#ifdef MPW /* MPW C modf expects pointer to extended as second argument */
|
||||
{
|
||||
extended e;
|
||||
fractpart = modf(v, &e);
|
||||
intpart = e;
|
||||
}
|
||||
#else
|
||||
fractpart = modf(v, &intpart);
|
||||
#endif
|
||||
if (fractpart == 0.0) {
|
||||
/* This must return the same hash as an equal int or long. */
|
||||
if (intpart > LONG_MAX || -intpart > LONG_MAX) {
|
||||
/* Convert to long and use its hash. */
|
||||
PyObject *plong; /* converted to Python long */
|
||||
if (Py_IS_INFINITY(intpart))
|
||||
/* can't convert to long int -- arbitrary */
|
||||
v = v < 0 ? -271828.0 : 314159.0;
|
||||
plong = PyLong_FromDouble(v);
|
||||
if (plong == NULL)
|
||||
return -1;
|
||||
x = PyObject_Hash(plong);
|
||||
Py_DECREF(plong);
|
||||
return x;
|
||||
}
|
||||
/* Fits in a C long == a Python int, so is its own hash. */
|
||||
x = (long)intpart;
|
||||
if (x == -1)
|
||||
x = -2;
|
||||
return x;
|
||||
}
|
||||
/* The fractional part is non-zero, so we don't have to worry about
|
||||
* making this match the hash of some other type.
|
||||
* Use frexp to get at the bits in the double.
|
||||
* Since the VAX D double format has 56 mantissa bits, which is the
|
||||
* most of any double format in use, each of these parts may have as
|
||||
* many as (but no more than) 56 significant bits.
|
||||
* So, assuming sizeof(long) >= 4, each part can be broken into two longs;
|
||||
* frexp and multiplication are used to do that.
|
||||
* Also, since the Cray double format has 15 exponent bits, which is the
|
||||
* most of any double format in use, shifting the exponent field left by
|
||||
* 15 won't overflow a long (again assuming sizeof(long) >= 4).
|
||||
* So, assuming sizeof(long) >= 4, each part can be broken into two
|
||||
* longs; frexp and multiplication are used to do that.
|
||||
* Also, since the Cray double format has 15 exponent bits, which is
|
||||
* the most of any double format in use, shifting the exponent field
|
||||
* left by 15 won't overflow a long (again assuming sizeof(long) >= 4).
|
||||
*/
|
||||
int expo;
|
||||
long hipart;
|
||||
|
||||
v = frexp(v, &expo);
|
||||
v = v * 2147483648.0; /* 2**31 */
|
||||
hipart = (long)v; /* Take the top 32 bits */
|
||||
v = (v - (double)hipart) * 2147483648.0; /* Get the next 32 bits */
|
||||
|
||||
return hipart + (long)v + (expo << 15); /* Combine everything */
|
||||
v = frexp(v, &expo);
|
||||
v *= 2147483648.0; /* 2**31 */
|
||||
hipart = (long)v; /* take the top 32 bits */
|
||||
v = (v - (double)hipart) * 2147483648.0; /* get the next 32 bits */
|
||||
x = hipart + (long)v + (expo << 15);
|
||||
if (x == -1)
|
||||
x = -2;
|
||||
return x;
|
||||
}
|
||||
|
||||
long
|
||||
|
||||
Reference in New Issue
Block a user