chiark / gitweb /
mp.c, catacomb/__init__.py, pyke/: Fix mixed-mode arithmetic involving `float'.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 22 Oct 2019 17:31:57 +0000 (18:31 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 11:44:21 +0000 (12:44 +0100)
This is a bit embarrassing.

>>> import catacomb as C
>>> x = C.MP(5)
>>> x == 5.1
True
>>> x < 5.1
False
>>> r = x/2
>>> r
5/2
>>> r == 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an integer is required
>>> r == 2.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an integer is required
>>> r*1.0
5/2
>>> r*1.1
5/2
>>> r*2.0
5
>>> r*2.5
5

Fix this nonsense.

  * Change the `obvious' arithmetic operators so that they notice that
    one of the operands is a float.  Handle this by converting to a
    Python bignum and letting Python handle the arithmetic.  The result
    is a float, which seems sensible inexact contagion.

  * Introduce a rich-comparison method which also detects a float
    operand and hands off to Python.  Python seems to get this right,
    comparing the float to the bignum in its full precision, so that's a
    win.

  * Also, modify the `IntRat' code to apply inexact contagion in the
    same way.  Comparisons may be imperfect here, but that's
    surprisingly hard to get right.

The new results:

>>> import catacomb as C
>>> x = C.MP(5)
>>> x == 5.1
False
>>> x < 5.1
True
>>> r = x/2
>>> r
5/2
>>> r == 2
False
>>> r == 2.5
True
>>> r*1.0
2.5
>>> r*1.1
2.75
>>> r*2.0
5.0
>>> r*2.5
6.25

pyke.c
pyke.h

diff --git a/pyke.c b/pyke.c
index cef4f822882d7036aaa1ce0dde7d067ba964be3d..1d4458b0239cdba71229241d77f6854b282f5db2 100644 (file)
--- a/pyke.c
+++ b/pyke.c
@@ -128,6 +128,22 @@ PyObject *abstract_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   return (0);
 }
 
+PyObject *enrich_compare(int op, int cmp)
+{
+  int r = -1;
+
+  switch (op) {
+    case Py_LT: r = cmp <  0; break;
+    case Py_LE: r = cmp <= 0; break;
+    case Py_EQ: r = cmp == 0; break;
+    case Py_NE: r = cmp != 0; break;
+    case Py_GE: r = cmp >= 0; break;
+    case Py_GT: r = cmp >  0; break;
+    default: assert(0);
+  }
+  return (getbool(r));
+}
+
 /*----- Saving and restoring exceptions ----------------------------------*/
 
 void report_lost_exception_v(struct excinfo *exc,
diff --git a/pyke.h b/pyke.h
index 654f7517439ccef52003f3f9a809f9985af6f2a7..cd135075acbaaad47fef25aeca4efd6eb912691e 100644 (file)
--- a/pyke.h
+++ b/pyke.h
@@ -279,6 +279,11 @@ extern PyObject *getulong(unsigned long); /* any kind of unsigned integer */
 extern PyObject *abstract_pynew(PyTypeObject *, PyObject *, PyObject *);
   /* A `tp_new' function which refuses to make the object. */
 
+extern PyObject *enrich_compare(int /*op*/, int /*cmp*/);
+  /* Use a traditional compare-against-zero comparison result CMP to answer a
+   * modern Python `tp_richcompare' operation OP.
+   */
+
 #ifndef CONVERT_CAREFULLY
 #  define CONVERT_CAREFULLY(newty, expty, obj)                         \
      (!sizeof(*(expty *)0 = (obj)) + (/*unconst*/ newty)(obj))