]> granicus.if.org Git - python/commitdiff
Fix for http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470.
authorTim Peters <tim.peters@gmail.com>
Tue, 15 Aug 2000 03:34:48 +0000 (03:34 +0000)
committerTim Peters <tim.peters@gmail.com>
Tue, 15 Aug 2000 03:34:48 +0000 (03:34 +0000)
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.

Include/pyport.h
Objects/complexobject.c
Objects/floatobject.c
Objects/longobject.c
Objects/object.c

index 3d9b305d914a2f48fab0117bf7b0606e915186e8..5bd641c9eda34b9d0c458a63b1eeb4b164545909 100644 (file)
@@ -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
index e8916d5adea5d2a35d3016ed21c5a2f7c7f69497..6f1cfea9233498098f7f14ede7689215f8d5bd49 100644 (file)
@@ -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 *
index 8182ae2ad8b05da54abb2293041044aab97c2639..26b39e89212e11a9d6bb2a2bbfeb1a428ee3e8f8 100644 (file)
@@ -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 *
index 37da2447991b09b17b5b9a94c74fddfcf0591639..86b4ababf53e93a9d6c49711ad0aaad09c2334ed 100644 (file)
@@ -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;
index a62c4481ed799ea276405d2c0515c3597a095dcf..d6b7607dd8812a852bd5b03313d09abf1264b240 100644 (file)
@@ -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