From: Mark Wooding Date: Tue, 22 Oct 2019 17:31:57 +0000 (+0100) Subject: mp.c, catacomb/__init__.py, pyke/: Fix mixed-mode arithmetic involving `float'. X-Git-Url: https://git.distorted.org.uk/~mdw/pyke/commitdiff_plain/1c0e9c88b06e3422c7afa0cf4bd3fbe166b401da mp.c, catacomb/__init__.py, pyke/: Fix mixed-mode arithmetic involving `float'. 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 "", line 1, in TypeError: an integer is required >>> r == 2.5 Traceback (most recent call last): File "", line 1, in 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 --- diff --git a/pyke.c b/pyke.c index cef4f82..1d4458b 100644 --- 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 654f751..cd13507 100644 --- 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))